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
- After one second,
timeout
sends SIGKILL
into its process group
- As the process group died,
bash
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.
timeout
is Linux specific, by the way. I get a return code of 127 with both code versions on 2.7.10 and 3.6.0 on OS X. – Meta