使用Popen.stdin执行多个命令
我想在一个独立的应用程序中执行多个命令,这个应用程序是通过一个Python脚本启动的,我想使用管道来实现。现在我能可靠地将命令传递给程序的标准输入(stdin)的方法是使用Popen.communicate,但这样做会在命令执行完后关闭程序。如果我使用Popen.stdin.write,那么命令大约只有五分之一的概率能成功执行,这样不太可靠。我到底哪里做错了呢?
再详细说一下:
我有一个应用程序,它会监听标准输入,逐行执行命令。我希望能够运行这个应用程序,并根据用户与图形界面的互动,传递不同的命令给它。这是一个简单的测试示例:
import os, string
from subprocess import Popen, PIPE
command = "anApplication"
process = Popen(command, shell=False, stderr=None, stdin=PIPE)
process.stdin.write("doSomething1\n")
process.stdin.flush()
process.stdin.write("doSomething2\n")
process.stdin.flush()
我本来希望能看到两个命令的结果,但我没有得到任何响应。(如果我多次执行其中一行Popen.write,有时会成功。)
如果我执行:
process.communicate("doSomething1")
它运行得很好,但应用程序会终止。
4 个回答
这里真正的问题是,应用程序是否在缓冲它的输出。如果是的话,你能做些什么来阻止这种情况。假设当用户生成一个命令并在你的图形界面上点击一个按钮时,你希望在用户输入下一个命令之前,能看到这个命令的输出。
不幸的是,在subprocess.Popen
的客户端,你无法确保当你给应用程序发送一个命令时,它会把所有的输出都及时显示出来。你可以随便调用flush()
,但如果应用程序不这样做,而你又无法控制它,那你就只能寻找其他解决办法了。
你在问题中写的代码应该是可以正常工作的。如果它不工作,那可能是因为你实际写的代码和这个不同(比如,你可能使用了 stdout=PIPE
,这可能会改变子进程的缓冲行为),或者可能是子程序本身有bug,比如 Python 2中的预读bug,也就是说,你的输入是由父进程正确发送的,但它卡在了子进程的内部输入缓冲区里。
在我的Ubuntu机器上,以下代码可以正常运行:
#!/usr/bin/env python
import time
from subprocess import Popen, PIPE
LINE_BUFFERED = 1
#NOTE: the first argument is a list
p = Popen(['cat'], bufsize=LINE_BUFFERED, stdin=PIPE,
universal_newlines=True)
with p.stdin:
for cmd in ["doSomething1\n", "doSomethingElse\n"]:
time.sleep(1) # a delay to see that the commands appear one by one
p.stdin.write(cmd)
p.stdin.flush() # use explicit flush() to workaround
# buffering bugs on some Python versions
rc = p.wait()
如果我理解你的问题没错的话,你是想和一个控制台应用程序进行互动,也就是发送命令并读取它的回复。
如果是这样的话,你可以看看类似Expect的库,比如Python的pexpect:http://pexpect.sourceforge.net
这个库会让你的工作变得简单,因为它会处理同步的问题,这也是ddaa提到的。你可以查看这个链接了解更多信息: http://www.noah.org/wiki/Pexpect#Q:_Why_not_just_use_a_pipe_.28popen.28.29.29.3F