我想从一个长时间运行的进程中捕获标准输出,subprocess.Popen(...)
因此我将stdout=PIPE
其用作 arg。
但是,因为这是一个长时间运行的过程,我还想将输出发送到控制台(好像我没有通过管道传输它一样),让脚本的用户知道它仍在工作。
这是可能吗?
干杯。
缓冲您的长时间运行的子进程可能正在执行会使您的控制台输出不稳定和非常糟糕的用户体验。我建议您考虑改用pexpect(或者,在 Windows 上,wexpect)来克服这种缓冲并从子进程中获得平滑、常规的输出。例如(在几乎任何 unix-y 系统上,安装 pexpect 之后):
>>> import pexpect
>>> child = pexpect.spawn('/bin/bash -c "echo ba; sleep 1; echo bu"', logfile=sys.stdout); x=child.expect(pexpect.EOF); child.close()
ba
bu
>>> child.before
'ba\r\nbu\r\n'
ba 和 bu 将在适当的时间出现(它们之间大约一秒钟)。请注意,输出不受正常终端处理的影响,因此回车符留在其中 -.replace
如果您需要\n
作为行尾标记(在子进程将二进制数据写入其标准输出的情况下,缺少处理很重要——这确保了所有数据都完好无损!-)。
S. Lott 的评论指出使用子进程获取实时输出和Python 中从另一个进程实时截取标准输出
我很好奇亚历克斯在这里的答案与他的答案 1085071 不同。我对其他两个参考问题的答案进行了简单的小实验,得到了很好的结果......
根据上面 Alex 的回答,我去查看了 wexpect,但我不得不说阅读代码中的注释后,我对使用它并没有很好的感觉。
我想这里的元问题是 pexpect/wexpect 什么时候会成为随附的电池之一?
print
当你从管道中读取它时,你能简单地理解它吗?
或者,您可以将您的流程通过管道传输到 tee 并仅捕获其中一个流。类似的东西sh -c 'process interesting stuff' | tee /dev/stderr
。
当然,这只适用于类 Unix 系统。
受上面某处 pty.openpty() 建议的启发,在 python2.6、linux 上进行了测试。发布,因为它需要一段时间才能使其正常工作,没有缓冲......
def call_and_peek_output(cmd, shell=False):
import pty, subprocess
master, slave = pty.openpty()
p = subprocess.Popen(cmd, shell=shell, stdin=None, stdout=slave, close_fds=True)
os.close(slave)
line = ""
while True:
try:
ch = os.read(master, 1)
except OSError:
# We get this exception when the spawn process closes all references to the
# pty descriptor which we passed him to use for stdout
# (typically when it and its childs exit)
break
line += ch
sys.stdout.write(ch)
if ch == '\n':
yield line
line = ""
if line:
yield line
ret = p.wait()
if ret:
raise subprocess.CalledProcessError(ret, cmd)
for l in call_and_peek_output("ls /", shell=True):
pass