当 运行 没有 shell=True 时缺少最后几行子进程输出

Missing last few lines of subprocess output when running without shell=True

我是 运行 一个简单的 Python 3.6 脚本,通过来自另一个 Python 3.6 脚本的子进程。我在原始进程中捕获 SIGINT 以将其传递给子进程。这只是为了测试一个最终会包装其他东西的模块,但这并不是太重要。

当我启动子进程 而没有 a shell 并发送 SIGINT 来停止它时,输出有时完全缺少子进程的回溯,其他时候它只是包含 Traceback (most recent call last): 行,有时它包含完整的回溯。

我正在使用 Popen,但我也验证了在使用 communicate() 或 check_output & co.

时会发生同样的事情

然而问题在于:如果我设置 shell=True 我总是在向子进程发送 SIGINT 时得到完整的回溯。

最小示例

producer.py

#!/usr/bin/env python3
import sys
import signal
import time

for number in range(100):
    sys.stderr.write(str(number) + 'this is line ' + str(number) + '\n')
    time.sleep(0.01)
    sys.stderr.flush()

consumer.py

import subprocess
import signal
import sys
import time

process = subprocess.Popen(['./producer.py'],
    stdout=subprocess.PIPE,
    stderr=subprocess.STDOUT,
    bufsize=1,
    #shell=True,
    encoding="latin-1",
    universal_newlines=True)

def stop_process(a,b):
    process.send_signal(signal.SIGINT)

old_handler = signal.signal(signal.SIGINT, stop_process)
s, e =  process.communicate()
print(s)
print('-----')
print(e)

尝试 运行 python3 consumer.py 并随时按 CTRL+C。如果您尝试几次,您应该会看到我列出的不同行为。现在在消费者中取消注释 shell=True 后重试,你应该每次都能得到完整的回溯。

有人知道这是为什么吗?我怀疑 python 解释器恶作剧,但我想了解发生了什么。

Ctrl-C 为所有涉及的进程提供 sigint 并且你的中继得到 child 命中两次(第二个当它忙于从第一个中死亡时导致它在没有输出的情况下保释你对第一个的期望)。 shell=True 在 parent 和 child 之间放置一个 shell。 child 仍然死于它的第一个签名,shell 忽略双重签名并仍在等待 child.