为什么subprocess.Popen返回码与bash的类似命令不同

Leh*_*ych 6 python bash subprocess dash-shell

为什么

import subprocess

p = subprocess.Popen(["/bin/bash", "-c", "timeout -s KILL 1 sleep 5 2>/dev/null"])
p.wait()
print(p.returncode)
Run Code Online (Sandbox Code Playgroud)

回报

[stderr:] /bin/bash: line 1: 963663 Killed                  timeout -s KILL 1 sleep 5 2> /dev/null
[stdout:] 137
Run Code Online (Sandbox Code Playgroud)

什么时候

import subprocess

p = subprocess.Popen(["/bin/bash", "-c", "timeout -s KILL 1 sleep 5"])
p.wait()
print(p.returncode)
Run Code Online (Sandbox Code Playgroud)

回报

[stdout:] -9
Run Code Online (Sandbox Code Playgroud)

如果你将bash改为破折号,那么在这两种情况下你都会得到137.我知道-9是KILL代码而137是128 + 9.但类似的代码获得不同的返回码似乎很奇怪.

发生在Python 2.7.12和python 3.4.3上

看起来在使用时Popen.wait()不会调用Popen._handle_exitstatus https://github.com/python/cpython/blob/3.4/Lib/subprocess.py#L1468,/bin/bash但我无法弄清楚原因.

use*_*968 4

这是因为在有或没有重定向/管道或任何其他 bash 功能的情况下如何bash执行:timeout

  • 带重定向

    1. python开始bash
    2. bash启动timeout,监视进程并进行管道处理。
    3. timeout将自身转移到新的进程组并启动sleep
    4. 一秒后,timeout发送SIGKILL到其进程组
    5. 当进程组死亡时,bash从等待返回timeout,看到SIGKILL并将上面粘贴的消息打印到stderr。然后它将自己的退出状态设置为 128+9(由 模拟的行为timeout)。
  • 不带重定向

    1. python开始bash
    2. bash认为它与自己无关,并呼吁execve()有效地用 来取代它自己timeout
    3. timeout如上所述,整个进程组会随着 一起死亡SIGKILL
    4. pythonget 的退出状态9并进行一些修改以将其变成-9( SIGKILL)

换句话说,没有重定向/管道/等。bash从调用链中退出。您的第二个示例看起来subprocess.Popen()正在执行bash,但实际上并没有。bash它的行为何时不再存在timeout,这就是为什么您没有收到任何消息和完整的退出状态。

如果您想要一致的行为,请使用timeout --foreground; 在这两种情况下,您都会得到 124 的退出状态。

我不知道破折号;但假设它没有做任何execve()欺骗来有效地用它正在执行的唯一程序替换自己。因此,您总是会在破折号中看到 128+9 的损坏退出状态。

更新:显示相同的行为,但即使对于简单的重定向(例如等)zsh它也会退出,为您提供 -9 的退出状态。也会给你状态 137 。timeout -s KILL 1 sleep 5 >/tmp/footimeout -s KILL 1 sleep 5 && echo $?zsh