When a process exits abnormally or not at all, I still want to be able to gather what output it may have generated up until that point.
The obvious solution to this
I had the exact same problem. I ended up fixing the issue (after scouring Google and finding many related problems) by simply setting the following parameters when calling subprocess.Popen
(or .call
):
stdout=None
and
stderr=None
There are many problems with these functions but in my specific case I believe stdout
was being filled up by the process I was calling and then resulting in a blocking condition. By setting these to None
(opposed to something like subprocess.PIPE
) I believe this is avoided.
Hope this helps someone.
There are good tips in another stackoverflow question: How do I get 'real-time' information back from a subprocess.Popen in python (2.5)
Most of the hints in there work with pipe.readline()
instead of pipe.communicate()
because the latter only returns at the end of the process.
Here's a POSIX way of doing it without the temporary file. I realize that subprocess is a little superfluous here, but since the original question used it...
import subprocess
import os
import time
import signal
import sys
pr, pw = os.pipe()
pid = os.fork ()
if pid: #parent
os.close(pw)
cmd = ["bash"]
finish = time.time() + 3
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=pr, close_fds=True)
while p.poll() is None:
time.sleep(0.05)
if finish < time.time():
os.kill(p.pid, signal.SIGTERM)
print "timed out and killed child, collecting what output exists so far"
out, err = p.communicate()
print "got it: ", out
sys.exit(0)
else: #child
os.close(pr)
child_script = """
#!/bin/bash
while [ 1 ]; do
((++i))
echo "output line $i"
sleep 1
done
"""
os.write(pw, child_script)
Problem is that bash doesn't answer to CTRL-C when not connected with a terminal. Switching to SIGHUP or SIGTERM seems to do the trick:
cmd = ["bash", 'childProc.sh']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
close_fds=True)
time.sleep(3)
print 'killing pid', p.pid
os.kill(p.pid, signal.SIGTERM)
print "timed out and killed child, collecting what output exists so far"
out = p.communicate()[0]
print "got it", out
Outputs:
killing pid 5844
timed out and killed child, collecting what output exists so far
got it output line 0
output line 1
output line 2