56

I am using subprocess.check_output from pythons subprocess module to execute a ping command. Here is how I am doing it:

output = subprocess.check_output(["ping","-c 2 -W 2","1.1.1.1")

It is raising a CalledProcessError and says the output is one of the arguments of the function. Can anyone help me how to read that output. I would like to read the output into a string and parse it. So say for example if the ping returns

100% packet loss

I need to capture that. If there is any other better way..please suggest. Thanks.

ash
  • 781
  • 1
  • 9
  • 20
  • 1
    // , Would you include a link to the documentation for this, https://docs.python.org/2/library/subprocess.html#subprocess.CalledProcessError, in this question? – Nathan Basanese Aug 31 '15 at 19:58
  • most important is to close any bracket you open: for each [,{ or ( there must be one ),} or ] and then imagine your shell command as a string and do `.split(' ')` on that and pass the resulting list as input to `check_output()` – krysopath Apr 15 '18 at 20:49

6 Answers6

171

According to the Python os module documentation os.popen has been deprecated since Python 2.6.

I think the solution for modern Python is to use check_output() from the subprocess module.

From the subprocess Python documentation:

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False) Run command with arguments and return its output as a byte string.

If the return code was non-zero it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute and any output in the output attribute.

If you run through the following code in Python 2.7 (or later):

import subprocess

try:
    print subprocess.check_output(["ping", "-n", "2", "-w", "2", "1.1.1.1"])
except subprocess.CalledProcessError, e:
    print "Ping stdout output:\n", e.output

You should see an output that looks something like this:

Ping stdout output:

Pinging 1.1.1.1 with 32 bytes of data:
Request timed out.
Request timed out.

Ping statistics for 1.1.1.1:
Packets: Sent = 2, Received = 0, Lost = 2 (100% loss),

The e.output string can be parsed to suit the OPs needs.

If you want the returncode or other attributes, they are in CalledProccessError as can be seen by stepping through with pdb

(Pdb)!dir(e)   

['__class__', '__delattr__', '__dict__', '__doc__', '__format__',
 '__getattribute__', '__getitem__', '__getslice__', '__hash__', '__init__',
 '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
 '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', 
 '__unicode__', '__weakref__', 'args', 'cmd', 'message', 'output', 'returncode']
laffuste
  • 16,287
  • 8
  • 84
  • 91
krd
  • 2,096
  • 2
  • 14
  • 11
  • 3
    note: use `check_call` instead of `check_output` if you don't use the output. If you know that the command returns non-zero exit status in ordinary cases then you could use `p = Popen()` directly and call `p.wait()` or `output, err = p.communicate()` (if you need the output) to avoid raising unnecessary exceptions – jfs May 06 '14 at 12:37
  • @J.F.Sebastian I try to use subprocess.call but this return me a int (1) and not the output of my command :/ – Loretta Jul 13 '15 at 09:01
  • @Loretta: notice: *"if you don't use the output"* in my comment. If `subprocess.call()` returns `1` then it means your command ran and it failed: usually it indicates an error, use `check_*` function to raise an exception in such cases. If it is not an error in your case (e.g., it means "not found" for `grep`); catch `CalledProcessError` or use `Popen` and its methods directly. – jfs Jul 13 '15 at 13:08
  • 2
    Thanks for wrapping in try except. – jorfus May 13 '16 at 23:30
  • What is stored in the output? If running, for example a shell script then is it the value in echo / cat command or something else? – arqam Sep 25 '18 at 09:15
  • 1
    The code snippet only works on Python 2.7, not Python 3, so you should probably remove "(or later)" from the statement preceding it. – kbolino Dec 02 '19 at 19:01
  • As of Python 3.3 or so, `os.popen` is no longer deprecated, although it is now basically just a wrapper around `subprocess.Popen`. The recommended function to use is `subprocess.run` for most scenarios, though the old and somewhat clumsy `call`, `check_call`, and `check_output` functions still work. – tripleee Oct 13 '20 at 09:20
26

If you want to get stdout and stderr back (including extracting it from the CalledProcessError in the event that one occurs), use the following:

import subprocess

def call_with_output(command):
    success = False
    try:
        output = subprocess.check_output(command, stderr=subprocess.STDOUT).decode()
        success = True 
    except subprocess.CalledProcessError as e:
        output = e.output.decode()
    except Exception as e:
        # check_call can raise other exceptions, such as FileNotFoundError
        output = str(e)
    return(success, output)

call_with_output(["ls", "-l"])

This is Python 2 and 3 compatible.

If your command is a string rather than an array, prefix this with:

import shlex
call_with_output(shlex.split(command))
Zags
  • 37,389
  • 14
  • 105
  • 140
21

In the list of arguments, each entry must be on its own. Using

output = subprocess.check_output(["ping", "-c","2", "-W","2", "1.1.1.1"])

should fix your problem.

avasal
  • 14,350
  • 4
  • 31
  • 47
phihag
  • 278,196
  • 72
  • 453
  • 469
  • I am still getting the error. The error says the output is one of the arguments of CalledProcessError. I tried using try except...but that did not work either. – ash Sep 28 '11 at 13:19
  • @ash Can you post a [short reproducible example](http://sscce.org/) that demonstrates `except` not working? – phihag Sep 28 '11 at 13:25
  • 1
    try:subprocess.check_output(["ping", "-c","2", "-W","2", "1.1.1.1") except CalledProcessError as e: print str(e.output). This is my try catch block – ash Sep 28 '11 at 14:17
  • Dont worry about the question...i figured out a work around by using Popen....and reading the output. Thanks for you help though. – ash Sep 28 '11 at 15:07
  • 3
    @ash, jfyi it should be `except subprocess.CalledProcessError` – Lelouch Lamperouge Sep 05 '13 at 00:08
  • 6
    This doesn't answer the question. – fiatjaf Oct 07 '14 at 21:50
2

I ran into the same problem and found that the documentation has example for this type of scenario (where we write STDERR TO STDOUT and always exit successfully with return code 0) without causing/catching an exception.

output = subprocess.check_output("ping -c 2 -W 2 1.1.1.1; exit 0", stderr=subprocess.STDOUT, shell=True)

Now, you can use standard string function find to check the output string output.

Pavin Joseph
  • 123
  • 1
  • 6
1

Thanx @krd, I am using your error catch process, but had to update the print and except statements. I am using Python 2.7.6 on Linux Mint 17.2.

Also, it was unclear where the output string was coming from. My update:

import subprocess

# Output returned in error handler
try:
    print("Ping stdout output on success:\n" + 
           subprocess.check_output(["ping", "-c", "2", "-w", "2", "1.1.1.1"]))
except subprocess.CalledProcessError as e:
    print("Ping stdout output on error:\n" + e.output)

# Output returned normally
try:
    print("Ping stdout output on success:\n" + 
           subprocess.check_output(["ping", "-c", "2", "-w", "2", "8.8.8.8"]))
except subprocess.CalledProcessError as e:
    print("Ping stdout output on error:\n" + e.output)

I see an output like this:

Ping stdout output on error:
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.

--- 1.1.1.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1007ms


Ping stdout output on success:
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=59 time=37.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=59 time=38.8 ms

--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 37.840/38.321/38.802/0.481 ms
AndruWitta
  • 186
  • 3
  • 6
1

This will return true only if host responds to ping. Works on windows and linux

def ping(host):
    """
    Returns True if host (str) responds to a ping request.
    NB on windows ping returns true for success and host unreachable
    """
    param = '-n' if platform.system().lower()=='windows' else '-c'
    result = False
    try:
        out = subprocess.check_output(['ping', param, '1', host])
        #ping exit code 0
        if 'Reply from {}'.format(host) in str(out):
            result = True          
    except  subprocess.CalledProcessError:
        #ping exit code not 0
            result = False
    #print(str(out))
    return result
vode
  • 11
  • 1