14
votes

I'd like a good method that matches the interface of subprocess.check_call -- ie, it throws CalledProcessError when it fails, is synchronous, &c -- but instead of returning the return code of the command (if it even does that) returns the program's output, either only stdout, or a tuple of (stdout, stderr).

Does somebody have a method that does this?

3
I linked this as a duplicate from a question where the OP tried to capture the output from os.system. Briefly, there is no way to do that; os.system runs a command completely outside of Python's control, and simply returns its exit code (zero for success, 1-255 for failure). The proper solution is to switch to subprocess.run() and friends, where you get this control. (There are several old questions with accepted answers which suggest os.popen() but that was the wrong answer for a long time, and now simply an obscure wrapper which calls subprocess anyway.)tripleee
It is not exact duplicate. check_call() does not run shell unless you explicitly ask it.jfs
Look into simppl pip module stackoverflow.com/a/643817120x90

3 Answers

25
votes

Python 2.7+

from subprocess import check_output as qx

Python < 2.7

From subprocess.py:

import subprocess
def check_output(*popenargs, **kwargs):
    if 'stdout' in kwargs:
        raise ValueError('stdout argument not allowed, it will be overridden.')
    process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
    output, unused_err = process.communicate()
    retcode = process.poll()
    if retcode:
        cmd = kwargs.get("args")
        if cmd is None:
            cmd = popenargs[0]
        raise subprocess.CalledProcessError(retcode, cmd, output=output)
    return output

class CalledProcessError(Exception):
    def __init__(self, returncode, cmd, output=None):
        self.returncode = returncode
        self.cmd = cmd
        self.output = output
    def __str__(self):
        return "Command '%s' returned non-zero exit status %d" % (
            self.cmd, self.returncode)
# overwrite CalledProcessError due to `output` keyword might be not available
subprocess.CalledProcessError = CalledProcessError

See also Capturing system command output as a string for another example of possible check_output() implementation.

1
votes

I can not get formatting in a comment, so this is in response to J.F. Sebastian's answer

I found this very helpful so I figured I would add to this answer. I wanted to be able to work seamlessly in the code without checking the version. This is what I did...

I put the code above into a file called 'subprocess_compat.py'. Then in the code where I use subprocess I did.

import sys
if sys.version_info < (2, 7):
    import subprocess_compat
    subprocess.check_output = subprocess_compat.check_output

Now anywhere in the code I can just call 'subprocess.check_output' with the params I want and it will work regardless of which version of python I am using.

-1
votes

This function returns terminal output in the form of list of string.

import subprocess,sys
def output(cmd):
    cmd_l = cmd.split()
    output = subprocess.Popen(cmd_l, stdout=subprocess.PIPE).communicate()[0]
    output = output.decode("utf-8")
    return (output)