【发布时间】:2024-01-22 21:02:02
【问题描述】:
我正在尝试处理来自subprocess.Popen 调用的stdout 和stderr,该调用通过subprocess.PIPE 捕获两者,但想处理输出(例如在终端上打印它们)。
我见过的所有当前解决方案都将等待Popen 调用完成,以确保捕获所有stdout 和stderr,以便对其进行处理。
这是一个带有混合输出的示例 Python 脚本,我在实时(或尽可能实时地)处理它时似乎无法复制订单:
$ cat mix_out.py
import sys
sys.stdout.write('this is an stdout line\n')
sys.stdout.write('this is an stdout line\n')
sys.stderr.write('this is an stderr line\n')
sys.stderr.write('this is an stderr line\n')
sys.stderr.write('this is an stderr line\n')
sys.stdout.write('this is an stdout line\n')
sys.stderr.write('this is an stderr line\n')
sys.stdout.write('this is an stdout line\n')
似乎可行的一种方法是使用线程,因为这样读取将是异步的,并且可以在 subprocess 产生输出时进行处理。
当前的实现只是首先处理stdout,最后处理stderr,如果输出最初在两者之间交替,则可能具有欺骗性:
cmd = ['python', 'mix_out.py']
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
**kw
)
if process.stdout:
while True:
out = process.stdout.readline()
if out == '' and process.poll() is not None:
break
if out != '':
print 'stdout: %s' % out
sys.stdout.flush()
if process.stderr:
while True:
err = process.stderr.readline()
if err == '' and process.poll() is not None:
break
if err != '':
print 'stderr: %s' % err
sys.stderr.flush()
如果我运行上述(另存为out.py)来处理上面的mix_out.py 示例脚本,则流(如预期)按顺序处理:
$ python out.py
stdout: this is an stdout line
stdout: this is an stdout line
stdout: this is an stdout line
stdout: this is an stdout line
stderr: this is an stderr line
stderr: this is an stderr line
stderr: this is an stderr line
stderr: this is an stderr line
我知道某些系统调用可能会缓冲,我对此表示同意,我希望解决的一件事是尊重流发生时的顺序。
有没有一种方法可以同时处理来自subprocess 的stdout 和stderr 而无需 使用线程? (代码在无法使用线程的受限远程系统中执行)。
区分 stdout 和 stderr 是必须的(如示例输出所示)
理想情况下,最好不要使用额外的库(例如,我知道 pexpect 解决了这个问题)
很多例子都提到了select 的使用,但我没有想出一些可以保持输出顺序的东西。
【问题讨论】:
-
我真的想不出任何不使用线程的解决方案。您有什么特别的原因要避免使用它们吗?
-
如果您只想打印到终端,如果您取出
PIPEs,这是默认设置。它也不能保证完全正确的顺序,但你能把stdout和stderr读入同一个while True循环吗? -
订单需要保留。
-
@dano 我不能使用线程,因为这段代码会在无法生成线程的远程(受限)系统中执行
标签: python subprocess