为什么python.subprocess在proc.communicate()后挂起?
我有一个互动程序,叫做 my_own_exe
。首先,它会打印出 alive
,然后你输入 S\n
,接着它又会打印出 alive
。最后你输入 L\n
,它会进行一些处理然后退出。
但是,当我从下面的Python脚本调用这个程序时,它在打印出第一个'alive'后似乎就卡住了。
这里有没有人能告诉我为什么会这样?
// 在阅读了后续的回复后(谢谢大家),我把代码修改成了这样:
import subprocess
import time
base_command = "./AO_FelixStrategy_UnitTest --bats 31441 --chix 12467 --enxutp 31884 --turq 26372 --symbol SOGN --target_date " + '2009-Oct-16'
print base_command
proc2 = subprocess.Popen(base_command, shell=True , stdin=subprocess.PIPE,)
time.sleep(2);
print "aliv"
proc2.communicate('S\n')
print "alive"
time.sleep(6)
print "alive"
print proc2.communicate('L\n')
time.sleep(6)
现在程序在接收到第一个输入'S\n'后运行得很好,但接下来又停住了,第二个输入'L\n'似乎被忽略了。
有没有人能告诉我为什么会这样?
4 个回答
虽然上面的回答提到在进程结束后使用 process.communicate()
是安全的,但这里有个小提醒。
如果这个进程启动了其他子进程,那么在你用
kill()
杀掉父进程后,只要子进程还活着,process.communicate()
就会被阻塞。虽然process.wait()
可能会返回一个代码,表示进程已经退出,但如果你调用process.communicate()
,它还是会卡住。这是因为 UNIX 系统(比如 Linux 和 macOS)的工作方式。
如果你使用了标准库中的
multiprocessing.Manager
类,可能会悄悄地出现这种情况。Manager
会在后台启动一个子进程,这对开发者来说是看不见的,所以可能会让人感到意外。因为
Manager
启动的子进程没有设置 Process.daemon 标志,这个子进程会阻止整个进程组的terminate()
或kill()
,导致即使进程看似已经结束,process.communicate()
也无法正常工作。在这种情况下,进程处于一种“僵尸”状态,既不活着也不完全死去。
作为参考,我没有找到一个简单的方法来安全地使用 Manager
,如果你想用 process.communicate()
。
communicate()
这个函数会从标准输出(stdout)和标准错误(stderr)中读取数据,直到文件的末尾被到达,也就是说,它会一直等到你的程序结束。
与进程进行交互:向标准输入(stdin)发送数据。从标准输出(stdout)和标准错误(stderr)读取数据,直到文件结束。等待进程终止。
所以在 communicate()
执行完后,进程就已经结束了。
如果你想在进程没有停止的情况下进行读写:
绝对不要使用
shell=True
- 这样会多此一举地调用一个shell来执行你的程序,这样在你和程序之间就会多出一个进程。这会带来很多不好的副作用。默认情况下是shell=False
,所以你应该保持这个设置。把你的Popen
行改成:p = subprocess.Popen(["./AO_FelixStrategy_UnitTest", "--bats", "31441", "--chix", "12467", "--enxutp", "31884", "--turq", "26372", "--symbol", "SOGN", "--target_date", '2009-Oct-16'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
使用
p.stdin.write
向进程写入数据。使用p.stdout.read
从进程读取数据。- 如果调用
p.stdout.read
时没有数据可读,它会阻塞。如果调用p.stdin.write
时写入缓冲区已满,它也会阻塞。所以你必须确保有东西可以读或写 - 在Unix系统上,你可以使用select
来做到这一点。在Windows上,你不幸地需要使用线程。至少这就是Popen.communicate
在内部所做的。 - 如果你没有写
AO_FelixStrategy_UnitTest
,那么你可能会遇到其他问题:- 它可能是从其他地方读取,而不是标准输入。有些程序直接从终端读取,其他程序使用某些操作系统API来读取。这意味着写入到stdin的数据不会传递给程序。这在密码提示时经常发生。
- 记得要考虑
AO_FelixStrategy_UnitTest
的缓冲区。默认情况下,标准C的管道通信是有缓冲的,所以你可能在关闭输入端(通过p.stdin.close()
)之前看不到任何输出。除非AO_FelixStrategy_UnitTest
定期刷新输出。
这里有一些示例代码,基于你描述的内容。它可能会根据 AO_FelixStrategy_UnitTest
的开发方式而工作:
p = subprocess.Popen(["./AO_FelixStrategy_UnitTest",
"--bats", "31441", "--chix", "12467",
"--enxutp", "31884", "--turq", "26372",
"--symbol", "SOGN", "--target_date", '2009-Oct-16'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
output = p.communicate('S\nL\n')[0]
print output