python 子进程:使用 subprocess.PIPE 时输出顺序发生变化

python subprocess: order of output changes when using subprocess.PIPE

当我编写一个名为 outer.py 的 python 脚本时,其中包含

p = subprocess.Popen(['./inner.py'])
print('Called inner.py without options, waiting for process...')
p.wait()
print('Waited for inner.py without options')

p = subprocess.Popen(['./inner.py'], stdout=subprocess.PIPE)
print('Called inner.py with PIPE, communicating...')
b_out, b_err = p.communicate()
out = b_out.decode('utf8')
print('out is "{}"'.format(out))

还有一个 inner.py 包含

print("inner: Echoing Hallo")
p = subprocess.Popen(['echo', 'hallo'])
print("inner: Waiting for Echo to finish...")
p.wait()
print("inner: Waited for Echo")

我从终端调用 outer.py 时得到以下信息:

Called inner.py without options, waiting for process...
inner: Echoing Hallo
inner: Waiting for Echo to finish...
hallo
inner: Waited for Echo
Waited for inner.py without options

Called inner.py with PIPE, communicating...
out is "hallo
inner: Echoing Hallo
inner: Waiting for Echo to finish...
inner: Waited for Echo
"

为什么在使用 stdout=subprocess.PIPE 调用 inner.py 时,在捕获的输出中 "hallo" 出现在 "inner: Echoing Hallo" 之前?

我猜想,出于某种原因(与管道和 ttys 相关,请参阅 this comment),inner.py Python 进程的输出在您第一次使用时是无缓冲的调用它,并在你第二次调用它时进行缓冲。第一次使用无缓冲输出时,您会按照预期的顺序将结果写入您的 tty。第二次,通过缓冲,首先刷新 echo 命令的输出(因为 echo 运行并终止),然后立即显示 inner.py 进程的所有输出, 当 python 终止时。如果您禁用 inner.py 的输出缓冲,您应该在两种情况下获得相同的输出。

通过设置 PYTHONUNBUFFERED 环境变量,或使用 -u 开关调用 python,或在每个 [=19] 之后显式调用 sys.stdout.flush() 来禁用输出缓冲=](或 Python 3 上的 print(..., flush=True))。

管道和 ttys 行为之间的区别似乎是 general behaviour of stdio:输出到 ttys 是行缓冲的(因此,在您的代码中,逐行读取,它似乎是无缓冲的), 而管道的输出是缓冲的。