使用Python运行其他程序

8

我有一个在命令行上运行良好的命令。它有许多参数,例如cmd --thing foo --stuff bar -a b input output

我想从Python中运行它并阻塞等待其完成。由于脚本将内容打印到stdoutstderr,因此我希望立即向用户显示。

哪个模块是正确的?

我尝试过:


import commands
output = commands.getoutput("cmd --thing foo --stuff bar -a b input output")
print output

这个方法很好,但是stdout只有在结束时才返回。


import os
os.system("cmd --thing foo --stuff bar -a b input output")

当命令实际完成时,这将打印所有输出。


import subprocess
subprocess.call(["cmd", "--thing foo", "--stuff bar", "-a b", "input", "output"])

某些情况下,这不会正确地传递参数(我还没有找到具体的问题,但是cmd拒绝我的输入)。如果我将echo作为第一个参数放置,它会打印出命令,当我直接粘贴到终端时,它可以完美地工作。


import subprocess
subprocess.call("cmd --thing foo --stuff bar -a b input output")

与上述内容完全相同。

4个回答

7
您需要分别引用每个字段,即将选项与其参数分开。
import subprocess
output = subprocess.call(["cmd", "--thing", "foo", "--stuff", "bar", "-a", "b", "input", "output"])

否则,您实际上在像这样运行 cmd。
$ cmd --thing\ foo --stuff\ bar -a\ b input output

为了将输出导入到管道中,您需要稍微改变调用方式。
import subprocess
output = subprocess.Popen(["cmd", "--thing", "foo", "--stuff", "bar", "-a", "b", "input", "output"],stdout=subprocess.PIPE)
output.stdout   #  <open file '<fdopen>', mode 'rb'>

subprocess 似乎没有按照返回的输出进行打印。我能让它发生吗? - Paul Tarjan
@Paul Tarjan:这是一个重复的问题。请搜索“[python] subprocess”,你会得到很多关于那个问题的答案。 - S.Lott

4
如果您不需要在代码中处理输出,只需将它显示给用户(从您的问题中并不清楚,但似乎是这样),最简单的方法是:
rc = subprocess.call(
    ["cmd", "--thing", "foo", "--stuff", "bar", 
     "-a", "b", "input", "output"])
print "Return code was", rc

即避免使用管道,让标准输出和标准错误直接显示在终端上。这样就可以避免缓冲问题。一旦引入管道,如果想要实时显示输出,通常会出现缓冲问题(我很惊讶你的自我回答没有这个问题;-))。顺便提一下,无论是显示还是捕获,我总是建议使用pexpect(在Windows上使用wexpect)来解决缓冲问题。

一如既往,感谢Alex。似乎subprocess.call()会缓冲其他程序的输出,这不是该程序通常在终端上的行为。我应该只是运行proc = subprocess.Popen并在proc.stdout上进行for循环以打印输出吗? - Paul Tarjan
1
@Paul,不是subprocess在做缓冲,而是当它注意到输出将进入管道而不是终端时,另一个程序的C运行时库会进行缓冲(您观察到的情况令人惊讶,因为stdout确实进入了终端,所以不应该发生缓冲)。 pexpect使用伪终端来欺骗其他程序的运行时库,从而阻止它们进行缓冲(在Windows上是wexpect)-我已经多次推荐过它们,请搜索我的其他答案以获取它们的URL。 - Alex Martelli

2
您可以尝试使用commands.getstatusoutput()命令,它会立即返回您的状态信息。

1
它确实可以,但请注意它仅适用于POSIX系统(不支持Windows)。 - jathanism

1
一个同事刚刚向我展示了这个:
import os
import sys
for line in os.popen("cmd --thing foo --stuff bar -a b input output", "r"):
    print line
    sys.stdout.flush()

它正在运行 :)


2
你应该优先使用subprocess而不是popen。首先,它可以避免让你担心参数转义的问题。 - John La Rooy
1
另一个原因是:自从Python 2.6版本起,os.popen()已被标记为弃用,建议使用subprocess代替(请参阅http://docs.python.org/library/os.html#os.popen)。 - Oben Sonne

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