Why
import subprocess
p = subprocess.Popen(["/bin/bash", "-c", "timeout -s KILL 1 sleep 5 2>/dev/null"])
p.wait()
print(p.returncode)
returns
[stderr:] /bin/bash: line 1: 963663 Killed timeout -s KILL 1 sleep 5 2> /dev/null
[stdout:] 137
when
import subprocess
p = subprocess.Popen(["/bin/bash", "-c", "timeout -s KILL 1 sleep 5"])
p.wait()
print(p.returncode)
returns
[stdout:] -9
If you change bash to dash, you'll get 137 in both cases. I know that -9 is KILL code and 137 is 128 + 9. But seems weird for similar code to get different returncode.
Happens on Python 2.7.12 and python 3.4.3
Looks like Popen.wait()
does not call Popen._handle_exitstatus
https://github.com/python/cpython/blob/3.4/Lib/subprocess.py#L1468 when using /bin/bash
but I could not figure out why.
The main difference is that subprocess. run() executes a command and waits for it to finish, while with subprocess. Popen you can continue doing your stuff while the process finishes and then just repeatedly call Popen. communicate() yourself to pass and receive data to your process.
Popen FunctionThe function should return a pointer to a stream that may be used to read from or write to the pipe while also creating a pipe between the calling application and the executed command. Immediately after starting, the Popen function returns data, and it does not wait for the subprocess to finish.
Popen is nonblocking. call and check_call are blocking. You can make the Popen instance block by calling its wait or communicate method.
Popen do we need to close the connection or subprocess automatically closes the connection? Usually, the examples in the official documentation are complete. There the connection is not closed. So you do not need to close most probably.
This is due to the fact how bash
executes timeout
with or without redirection/pipes or any other bash features:
With redirection
python
starts bash
bash
starts timeout
, monitors the process and does pipe handling.timeout
transfers itself into a new process group and starts sleep
timeout
sends SIGKILL
into its process groupbash
returns from waiting for timeout
, sees the SIGKILL
and prints the message pasted above to stderr
. It then sets its own exit status to 128+9 (a behaviour simulated by timeout
).Without redirection
python
starts bash
.bash
sees that it has nothing to do on its own and calls execve()
to effectively replace itself with timeout
.timeout
acts as above, the whole process group dies with SIGKILL
.python
get's an exit status of 9
and does some mangling to turn this into -9
(SIGKILL
)In other words, without redirection/pipes/etc. bash
withdraws itself from the call-chain. Your second example looks like subprocess.Popen()
is executing bash
, yet effectively it does not. bash
is no longer there when timeout
does its deed, which is why you don't get any messages and an unmangled exit status.
If you want consistent behaviour, use timeout --foreground
; you'll get an exit status of 124 in both cases.
I don't know about dash; yet suppose it does not do any execve()
trickery to effectively replace itself with the only program it's executing. Therefore you always see the mangled exit status of 128+9 in dash.
Update: zsh
shows the same behaviour, while it drops out even for simple redirections such as timeout -s KILL 1 sleep 5 >/tmp/foo
and the like, giving you an exit status of -9. timeout -s KILL 1 sleep 5 && echo $?
will give you status 137 in zsh
also.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With