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

jan*_*jan 6 python subprocess pipe python-3.5

outer.py当我编写一个名为contains的 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))
Run Code Online (Sandbox Code Playgroud)

以及一个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")
Run Code Online (Sandbox Code Playgroud)

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
"
Run Code Online (Sandbox Code Playgroud)

为什么在使用 调用时inner.pystdout=subprocess.PIPE“hallo”出现在捕获的输出中的“inner: Echoing Hallo”之前?

wil*_*elm 7

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

通过设置PYTHONUNBUFFERED环境变量、使用开关调用 python-usys.stdout.flush()在 every 之后显式调用print(或print(..., flush=True)在 Python 3 上)来禁用输出缓冲。

管道和 ttys 行为之间的差异似乎是以下一般行为stdio:输出到 ttys 是行缓冲的(因此,在您的代码中,逐行读取,它似乎是未缓冲的),而输出到管道是缓冲的。