59

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.

2
  • 1
    // , Would you include a link to the documentation for this, docs.python.org/2/library/…, in this question? Commented Aug 31, 2015 at 19:58
  • 1
    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() Commented Apr 15, 2018 at 20:49

6 Answers 6

175

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 OP's 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']
Sign up to request clarification or add additional context in comments.

6 Comments

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
@J.F.Sebastian I try to use subprocess.call but this return me a int (1) and not the output of my command :/
@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.
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?
The code snippet only works on Python 2.7, not Python 3, so you should probably remove "(or later)" from the statement preceding it.
|
30

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))

3 Comments

e.output.decode("utf-8") returns "" on my end
@alper Processes can fail without generating any output. Here's a trivial example: call_with_output(["false"])
Using .decode('utf-8') instead of relying on the default encoding is a good idea if you know the output will be text.
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.

6 Comments

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 Can you post a short reproducible example that demonstrates except not working?
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, jfyi it should be except subprocess.CalledProcessError
This doesn't answer the question.
|
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.

Comments

2

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

Comments

1

Thanks krd; I am using your error catch process, but I 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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.