I've been using subprocess.check_output()
for some time to capture output from subprocesses, but ran into some performance problems under certain circumstances. I'm running this on a RHEL6 machine.
The calling Python environment is linux-compiled and 64-bit. The subprocess I'm executing is a shell script which eventually fires off a Windows python.exe process via Wine (why this foolishness is required is another story). As input to the shell script, I'm piping in a small bit of Python code that gets passed off to python.exe.
While the system is under moderate/heavy load (40 to 70% CPU utilization), I've noticed that using subprocess.check_output(cmd, shell=True)
can result in a significant delay (up to ~45 seconds) after the subprocess has finished execution before the check_output command returns. Looking at output from ps -efH
during this time shows the called subprocess as sh <defunct>
, until it finally returns with a normal zero exit status.
Conversely, using subprocess.call(cmd, shell=True)
to run the same command under the same moderate/heavy load will cause the subprocess to return immediately with no delay, all output printed to STDOUT/STDERR (rather than returned from the function call).
Why is there such a significant delay only when check_output()
is redirecting the STDOUT/STDERR output into its return value, and not when the call()
simply prints it back to the parent's STDOUT/STDERR?
Best Solution
Reading the docs, both
subprocess.call
andsubprocess.check_output
are use-cases ofsubprocess.Popen
. One minor difference is thatcheck_output
will raise a Python error if the subprocess returns a non-zero exit status. The greater difference is emphasized in the bit aboutcheck_output
(my emphasis):So how is
stdout
"used internally"? Let's comparecall
andcheck_output
:call
check_output
communicate
Now we have to look at
Popen.communicate
as well. Doing this, we notice that for one pipe,communicate
does several things which simply take more time than simply returningPopen().wait()
, ascall
does.For one thing,
communicate
processesstdout=PIPE
whether you setshell=True
or not. Clearly,call
does not. It just lets your shell spout whatever... making it a security risk, as Python describes here.Secondly, in the case of
check_output(cmd, shell=True)
(just one pipe)... whatever your subprocess sends tostdout
is processed by a thread in the_communicate
method. AndPopen
must join the thread (wait on it) before additionally waiting on the subprocess itself to terminate!Plus, more trivially, it processes
stdout
as alist
which must then be joined into a string.In short, even with minimal arguments,
check_output
spends a lot more time in Python processes thancall
does.