我编写了一个脚本,在命令行上使用 nc 访问一堆服务器,最初我使用 Python 的命令模块并调用 commands.getoutput()。该脚本运行时间约为 45 秒。由于 commands 已弃用,我想将所有内容更改为使用 subprocess 模块,但现在脚本需要 2 分 45 秒才能运行。为什么会这样?
我之前拥有的:
output = commands.getoutput("echo get file.ext | nc -w 1 server.com port_num")
现在我有了
p = Popen('echo get file.ext | nc -w 1 server.com port_num', shell=True, stdout=PIPE)
output = p.communicate()[0]
我预计
subprocess
会比 command
慢。无意暗示这是您的脚本运行缓慢的唯一原因,您应该查看commands
源代码。不到 100 行,大部分工作都委托给 os
中的函数,其中许多直接取自 c posix 库(至少在 posix 系统中)。请注意,commands
仅适用于unix,因此它不必做任何额外的工作来确保跨平台兼容性。
subprocess
。有超过 1500 行,全部是纯 Python,进行各种检查以确保一致的跨平台行为。基于此,我预计 subprocess
的运行速度会比 commands
慢。
我对这两个模块进行了计时,在一些非常基本的事情上,
subprocess
几乎是commands
慢的两倍。
>>> %timeit commands.getoutput('echo "foo" | cat')
100 loops, best of 3: 3.02 ms per loop
>>> %timeit subprocess.check_output('echo "foo" | cat', shell=True)
100 loops, best of 3: 5.76 ms per loop
Swiss 建议了一些很好的改进,这将有助于提高脚本的性能。但即使在应用它们之后,请注意
subprocess
仍然较慢。
>>> %timeit commands.getoutput('echo "foo" | cat')
100 loops, best of 3: 2.97 ms per loop
>>> %timeit Popen('cat', stdin=PIPE, stdout=PIPE).communicate('foo')[0]
100 loops, best of 3: 4.15 ms per loop
假设您连续多次执行上述命令,这会累加起来,并至少解释部分性能差异。
无论如何,我将您的问题解释为关于
subprocess
和
command
的相对性能,而不是关于如何加快脚本速度。对于后一个问题,Swiss的回答比较好。
首先,您使用Popen的方式不当。以下是我看到的问题:
from subprocess import PIPE
args = ['nc', '-w', '1', 'server.com', 'port_num']
p = subprocess.Popen(args, stdin=PIPE, stdout=PIPE)
output = p.communicate("get file.ext")
print output[0]
其次,您建议手动运行时比通过子进程运行时结束得更快这一事实表明,这里的问题是您没有将正确的字符串传递给
nc
。可能发生的情况是服务器正在等待终止字符串来结束连接。如果您没有传递此参数,则连接可能会保持打开状态,直到超时。手动运行
nc
,找出终止字符串是什么,然后更新传递给
communicate
的字符串。通过这些更改,它应该运行得更快。