进程终止后Python通信被阻止

进程终止后Python通信被阻止,python,linux,bash,subprocess,Python,Linux,Bash,Subprocess,我正在运行一个执行Bash子进程的Python脚本。如果Bash子进程超时,那么Python脚本将打印Bash子进程的stdout。但是,如果使用“sudo”关键字执行Bash子进程,那么在超时后读取stddout会阻塞Python,那么Python脚本将按预期工作 Bash脚本(名为test Bash.sh)如下所示: #!/bin/sh while : do echo "Press [CTRL+C] to stop.." sleep 1 d

我正在运行一个执行Bash子进程的Python脚本。如果Bash子进程超时,那么Python脚本将打印Bash子进程的stdout。但是,如果使用“sudo”关键字执行Bash子进程,那么在超时后读取stddout会阻塞Python,那么Python脚本将按预期工作

Bash脚本(名为test Bash.sh)如下所示:

#!/bin/sh
while :
do
        echo "Press [CTRL+C] to stop.."
        sleep 1
done
import subprocess
proc = subprocess.Popen("sudo ./test-bash.sh", shell=True, stdout=subprocess.PIPE)
try:
    outs, errs = proc.communicate(timeout=3)
except subprocess.TimeoutExpired:
    proc.kill()
    print("Succesfully killed")
    outs, errs = proc.communicate()
    print("Stdout: {}".format(outs))

Python脚本如下所示:

#!/bin/sh
while :
do
        echo "Press [CTRL+C] to stop.."
        sleep 1
done
import subprocess
proc = subprocess.Popen("sudo ./test-bash.sh", shell=True, stdout=subprocess.PIPE)
try:
    outs, errs = proc.communicate(timeout=3)
except subprocess.TimeoutExpired:
    proc.kill()
    print("Succesfully killed")
    outs, errs = proc.communicate()
    print("Stdout: {}".format(outs))

除非我们从以下位置删除“sudo”,否则不会调用最后一次打印,在Communication()上被阻止:


阻止Communication()的原因是什么?如果必须使用“sudo”运行Bash子进程,如何取消阻止并读取stddout()。

如果
shell=True
,即使没有
sudo
,问题也可以重现。这就像约翰·博林格在上面解释的,所以我不会让我们背诵了。幸运的是,
Popen
提供了一种处理此类多级子流程的方法—它允许启动一个新会话,并由此创建一个流程组,通过该流程组可以终止所有子流程

# add start_new_session=True argument
proc = subprocess.Popen(…, stdout=subprocess.PIPE, start_new_session=True)
…
# replace proc.kill() by
    import os
    import signal
    os.killpg(proc.pid, signal.SIGTERM)

我的猜测是,当超时发生时,Python只杀死它启动的shell来运行
sudo
命令,而不是
sudo
命令本身或它开始运行shell脚本的shell
communicate()
则不会被阻止,就其本身而言,它只是在子进程的标准输出上看不到文件的结尾,因为另一端的shell仍在运行并保持写端打开。您可以尝试
subprocess.Popen(…,shell=False,…)
。我不确定您认为在Python和您首先运行的命令之间插入shell会带来什么好处。不过,这可能仍然不能解决问题,因为它只会剥下三层洋葱的外层。谢谢。关于杀死外壳的猜测是正确的,但在我的具体案例中,我无法摆脱外壳=真。Armali提供的解决方案提出了杀死整个流程组的方法,帮助解决了这个问题,不管shell参数设置为True还是False。它成功了!谢谢