Python的subprocess模块中,check_output函数比call函数执行速度要慢得多。

9
我试图理解为什么会发生这种情况。我正在调用一个命令来重新启动Ubuntu服务器12.04上的网络。
快速执行
当我使用以下三种方式之一调用命令时,执行时间大约为0.1秒:
1. 直接在终端中执行 2. 使用`os.system`的Python脚本 3. 使用`subprocess.call`的Python脚本
终端会话:
root@ubuntu:~# time /etc/init.d/networking restart
 * Running /etc/init.d/networking restart
 * Reconfiguring network interfaces...
real    0m0.105s

root@ubuntu:~# time python -c "import os;
> os.system('/etc/init.d/networking restart')"
 * Running /etc/init.d/networking restart
 * Reconfiguring network interfaces...
real    0m0.111s

root@ubuntu:~# time python -c "import subprocess;
> subprocess.call(['/etc/init.d/networking', 'restart'])"
 * Running /etc/init.d/networking restart
 * Reconfiguring network interfaces...
real    0m0.111s

执行缓慢

然而,如果我使用 subprocess.check_output 或者 Popen 并尝试读取输出,那么花费的时间将会是23秒。速度慢得多。这种显著差异似乎只发生在我尝试使用会返回命令输出的函数时。我想要了解为什么会发生这种情况,并找到一种解决方法来执行此命令并快速获取其输出。

终端会话:

root@ubuntu:~# time python -c "import subprocess;
> print subprocess.check_output(['/etc/init.d/networking', 'restart'])"
 * Running /etc/init.d/networking restart
 * Reconfiguring network interfaces...
real    0m23.201s

root@ubuntu:~# time python -c "from subprocess import Popen, PIPE;
> print Popen(['/etc/init.d/networking', 'restart'], stdout=PIPE).stdout.read()"
 * Running /etc/init.d/networking restart
 * Reconfiguring network interfaces...
real    0m23.201s

更新

其中一条评论建议尝试使用 tee 命令。结果非常有趣。在终端中,如果不涉及 Python,并使用 tee 命令,需要相同的 23 秒。我仍然很好奇为什么会这样,但至少这可能提供了更多关于发生情况的线索。

root@ubuntu:~# time /etc/init.d/networking restart | tee out.txt
 * Running /etc/init.d/networking restart
 * Reconfiguring network interfaces...
real    0m23.181s

我不知道以下两个问题是否相关([问题#10150368](https://dev59.com/4GLVa4cB1Zd3GeqPyq6Q),[问题#4940607](http:// stackoverflow.com / questions / 4940607 / python-subprocesses-experience-mysterious-delay-in-receiving-stdin-eof)),但其中一个答案建议在popen参数中添加close_fds = True - James Waldby - jwpat7
subprocess.call() 就是 subprocess.Popen(*popenargs, **kwargs).wait() - Blender
感谢@jwpat7提供的链接。我尝试使用close_fds=True,但没有任何区别。 - Marwan Alsabbagh
5
执行 networking restart | tee some_file 的时间需要多久(检查一下 some_file 是否为空)。尝试使用 bufsize=-1 和/或 f = TemporaryFile(); check_call(cmd, stdout=f, stderr=STDOUT); f.seek(0); output = f.read() - jfs
@J.F.Sebastian,你的建议太棒了。我在问题中发布了tee的结果,输出的文件不是空的,它包含了输出内容。你使用临时文件的方法非常好。如果你在下面发布它作为答案,我可以点赞。但我仍然不明白为什么会发生这种情况。 - Marwan Alsabbagh
1个回答

11
下面的代码基于J.F. Sebastian所做的卓越评论。以下代码在预期的0.1秒内运行,并将命令的输出返回为字符串。
from subprocess import check_call, STDOUT
from tempfile import NamedTemporaryFile

with NamedTemporaryFile() as f:
    check_call(['/etc/init.d/networking', 'restart'], stdout=f, stderr=STDOUT)
    f.seek(0)
    output = f.read()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接