Почему subprocess.Popen код возврата отличается для аналогичных команд с bash
Зачем
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 на dash, вы получите 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 при использовании /bin/bash
но я не мог понять почему.
1 ответ
Это связано с тем, как bash
исполняет timeout
с или без перенаправления / трубы или любые другие функции bash:
С перенаправлением
python
начинаетсяbash
bash
начинаетсяtimeout
, контролирует процесс и выполняет обработку труб.timeout
переносит себя в новую группу процессов и запускаетsleep
- Через одну секунду
timeout
посылаетSIGKILL
в свою группу процессов - Когда группа процессов умерла,
bash
возвращается из ожиданияtimeout
видитSIGKILL
и печатает сообщение, вставленное выше, вstderr
, Затем он устанавливает свой собственный статус выхода на 128+9 (поведение, моделируемоеtimeout
).
Без перенаправления
python
начинаетсяbash
,bash
видит, что он не имеет ничего общего и вызываетexecve()
эффективно заменить себяtimeout
,timeout
действует как выше, вся группа процессов умирает сSIGKILL
,python
получить статус выхода9
и делает некоторые искажения, чтобы превратить это в-9
(SIGKILL
)
Другими словами, без перенаправления / трубы / и т. Д. 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 $?
даст вам статус 137 в zsh
также.