【问题标题】:Python communicate is blocked after process kill进程终止后Python通信被阻止
【发布时间】:2021-04-01 16:05:48
【问题描述】:

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

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

#!/bin/sh
while :
do
        echo "Press [CTRL+C] to stop.."
        sleep 1
done

Python 脚本如下所示:

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))

最后一个 print 永远不会被调用,在通信()上被阻止,除非我们从以下位置删除“sudo”:

proc = subprocess.Popen("sudo ./test-bash.sh", shell=True, stdout=subprocess.PIPE)

communicate() 阻塞的原因是什么?如果我必须使用“sudo”运行 Bash 子进程,如何解除阻塞并读取 stddout()?

【问题讨论】:

  • 我的猜测是,当超时发生时,Python 只会杀死它开始运行sudo 命令的shell,而不是sudo 命令本身或它的shell,反过来,开始运行你的 shell 脚本。 communicate() 然后没有被阻塞,本身,它只是在子进程的标准输出中看不到文件结尾,因为另一端的 shell 仍在运行并保持写入端打开。
  • 你可以试试subprocess.Popen(..., shell=False, ...)。我不确定你认为在 Python 和你首先运行的命令之间插入一个 shell 会得到什么。然而,这可能仍然无法解决问题,因为它只会剥掉三层洋葱的外层。
  • 谢谢。关于杀死外壳的猜测是正确的,但在我的具体情况下,我无法摆脱 shell=True。 Armali 提供的建议 kill the entire process group 的解决方案帮助解决了这个问题,无论 shell 参数设置为 True 还是 False。

标签: python linux bash subprocess


【解决方案1】:

即使没有sudo,问题也可以重现,前提是shell=True。就像上面解释的约翰·布林格一样,所以我将省去我们的背诵。幸运的是,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)

【讨论】:

    猜你喜欢
    • 2014-07-12
    • 2016-11-10
    • 2020-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多