为什么subprocess.Popen返回码与bash

时间:2017-08-22 18:53:22

标签: 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)

返回

[stderr:] /bin/bash: line 1: 963663 Killed                  timeout -s KILL 1 sleep 5 2> /dev/null
[stdout:] 137

import subprocess

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

返回

[stdout:] -9

如果将bash更改为破折号,则两种情况下都会获得137。我知道-9是KILL代码而137是128 + 9.但类似的代码看起来很奇怪,以获得不同的返回码。

发生在Python 2.7.12和python 3.4.3

在使用Popen.wait()时,Popen._handle_exitstatus似乎没有调用/bin/bash https://github.com/python/cpython/blob/3.4/Lib/subprocess.py#L1468,但我无法弄清楚原因。

1 个答案:

答案 0 :(得分:4)

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

  • 使用重定向

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

    1. python启动bash
    2. bash发现它本身无关,并致电execve()以有效地将自己替换为timeout
    3. timeout如上所示,整个流程组以SIGKILL而死。
    4. python退出状态为9并进行一些修改以将其转换为-9SIGKILL

换句话说,没有重定向/管道/等。 bash退出了呼叫链。您的第二个示例看起来subprocess.Popen()正在执行bash,但实际上并非如此。当bash履行契约时,timeout已不再存在,这就是为什么您不会收到任何消息和无法退出的退出状态。

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

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

更新zsh显示相同的行为,即使对于timeout -s KILL 1 sleep 5 >/tmp/foo之类的简单重定向,它也会退出,退出状态为-9。 timeout -s KILL 1 sleep 5 && echo $?也会在zsh中为您提供状态137。