I am trying to grab stdout
from a subprocess.Popen
call and although I am achieving this easily by doing:
cmd = subprocess.Popen('ls -l', shell=True, stdout=PIPE)
for line in cmd.stdout.readlines():
print line
I would like to grab stdout
in "real time". With the above method, PIPE is waiting to grab all the stdout
and then it returns.
So for logging purposes, this doesn't meet my requirements (e.g. "see" what is going on while it happens).
Is there a way to get line by line, stdout
while is running? Or is this a limitation of subprocess
(having to wait until the PIPE
closes).
EDIT
If I switch readlines()
for readline()
I only get the last line of the stdout
(not ideal):
In [75]: cmd = Popen('ls -l', shell=True, stdout=PIPE)
In [76]: for i in cmd.stdout.readline(): print i
....:
t
o
t
a
l
1
0
4
As stated already the issue is in the stdio library's buffering of printf like statements when no terminal is attached to the process. There is a way around this on the Windows platform anyway. There may be a similar solution on other platforms as well.
On Windows you can force create a new console at process creation. The good thing is this can remain hidden so you never see it (this is done by shell=True inside the subprocess module).
or
A slightly more complete solution is that you explicitly set the STARTUPINFO params which prevents launching a new and unnecessary cmd.exe shell process which shell=True did above.
As this is a question I searched for an answer to for days, I wanted to leave this here for those who follow. While it is true that
subprocess
cannot combat the other process's buffering strategy, in the case where you are calling another Python script withsubprocess.Popen
, you CAN tell it to start an unbuffered python.I have also seen cases where the popen arguments
bufsize=1
anduniversal_newlines=True
have helped with exposing the hiddenstdout
.Drop the readlines() which is coalescing the output. Also you'll need to enforce line buffering since most commands will interally buffer output to a pipe. For details see: http://www.pixelbeat.org/programming/stdio_buffering/
The call to
readlines
is waiting for the process to exit. Replace this with a loop aroundcmd.stdout.readline()
(note singular) and all should be well.Actually, the real solution is to directly redirect the stdout of the subprocess to the stdout of your process.
Indeed, with your solution, you can only print stdout, and not stderr, for instance, at the same time.
The
communicate()
is so to make the call blocking until the end of the subprocess, else it would directly go to the next line and your program might terminate before the subprocess (although the redirection to your stdout will still work, even after your python script has closed, I tested it).That way, for instance, you are redirecting both stdout and stderr, and in absolute real time.
For instance, in my case I tested with this script
slow_cmd_output.sh
: