我正在尝试编写一个能够与其他程序交互的 python 程序。这意味着发送标准输入和接收标准输出数据。我不能使用 pexpect(尽管它确实启发了一些设计)。我现在使用的过程是这样的:
- 将 pty 附加到子进程的标准输出
- 循环直到子进程通过检查
subprocess.poll
退出- 当标准输出中有可用数据时,立即将该数据写入当前标准输出。
- 完成!
我一直在制作一些代码(如下)的原型(prototype),这些代码可以工作,但似乎有一个缺陷困扰着我。子进程完成后,如果我在使用 select.select
时未指定超时,则父进程会挂起。我真的不想设置超时。只是看起来有点脏。但是,我尝试解决该问题的所有其他方法似乎都不起作用。 Pexpect 似乎通过使用 os.execv
和 pty.fork
而不是 subprocess.Popen
和 pty.openpty来绕过它
我不喜欢的解决方案。我在检查子流程的生命周期时做错了什么吗?我的方法不正确吗?
我正在使用的代码如下。我在 Mac OS X 10.6.8 上使用它,但我也需要它在 Ubuntu 12.04 上工作。
这是子进程运行器 runner.py
:
import subprocess
import select
import pty
import os
import sys
def main():
master, slave = pty.openpty()
process = subprocess.Popen(['python', 'outputter.py'],
stdin=subprocess.PIPE,
stdout=slave, stderr=slave, close_fds=True)
while process.poll() is None:
# Just FYI timeout is the last argument to select.select
rlist, wlist, xlist = select.select([master], [], [])
for f in rlist:
output = os.read(f, 1000) # This is used because it doesn't block
sys.stdout.write(output)
sys.stdout.flush()
print "**ALL COMPLETED**"
if __name__ == '__main__':
main()
这是子进程代码 outputter.py
。 奇怪的随机部分只是为了模拟程序以随机间隔输出数据。如果您愿意,可以将其删除。没关系:
import time
import sys
import random
def main():
lines = ['hello', 'there', 'what', 'are', 'you', 'doing']
for line in lines:
sys.stdout.write(line + random.choice(['', '\n']))
sys.stdout.flush()
time.sleep(random.choice([1,2,3,4,5])/20.0)
sys.stdout.write("\ndone\n")
sys.stdout.flush()
if __name__ == '__main__':
main()
感谢大家提供的任何帮助!
额外说明
使用 pty 是因为我想确保不缓冲 stdout。
最佳答案
首先,os.read
确实阻止,与您所说的相反。但是,它不会在 select
之后阻塞.还有 os.read
在关闭的文件描述符上总是返回一个空字符串,您可能需要检查该字符串。
然而,真正的问题是主设备描述符永远不会关闭,因此最终的 select
是会阻塞的。在罕见的竞争条件下,子进程已在 select
之间退出。和 process.poll()
并且您的程序很好地退出。然而,大多数时候 select 会永远阻塞。
如果按照izhak 的建议安装信号处理程序,一切都会崩溃;每当子进程终止时,都会运行信号处理程序。信号处理程序运行后,该线程中的原始系统调用无法继续,因此系统调用调用返回非零的errno,这通常会导致python中抛出一些随机异常。现在,如果在你的程序的其他地方你使用一些库与不知道如何处理此类异常的任何阻塞系统调用,你就有大麻烦了(任何 os.read
例如任何地方现在都可以抛出异常,即使在成功之后select
).
权衡一下在任何地方抛出的随机异常与轮询相比,我不认为 select
上的超时听起来不错。无论如何,您的进程仍然很难成为系统上唯一的(缓慢的)轮询进程。
关于python - 捕获输出时,将子进程与 select 和 pty 一起使用会挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11165521/