10

I want to call ping from Python and get the output. I tried the following:

response = os.system("ping "+ "- c")

However, this prints to the console, which I don't want.

PING 10.10.0.100 (10.10.0.100) 56(86) bytes of data.
64 bytes from 10.10.0.100: icmp_seq=1 ttl=63 time=0.713 ms
64 bytes from 10.10.0.100: icmp_seq=2 ttl=63 time=1.15 ms

Is there a way to not print to the console and just get the result?

davidism
  • 121,510
  • 29
  • 395
  • 339
pelos
  • 1,744
  • 4
  • 24
  • 34

2 Answers2

15

To get the output of a command, use subprocess.check_output. It raises an error if the command fails, so surround it in a try block.

import subprocess

try:
    response = subprocess.check_output(
        ['ping', '-c', '3', '10.10.0.100'],
        stderr=subprocess.STDOUT,  # get all output
        universal_newlines=True  # return string not bytes
    )
except subprocess.CalledProcessError:
    response = None

To use ping to know whether an address is responding, use its return value, which is 0 for success. subprocess.check_call will raise and error if the return value is not 0. To suppress output, redirect stdout and stderr. With Python 3 you can use subprocess.DEVNULL rather than opening the null file in a block.

import os
import subprocess

with open(os.devnull, 'w') as DEVNULL:
    try:
        subprocess.check_call(
            ['ping', '-c', '3', '10.10.0.100'],
            stdout=DEVNULL,  # suppress output
            stderr=DEVNULL
        )
        is_up = True
    except subprocess.CalledProcessError:
        is_up = False

In general, use subprocess calls, which, as the docs describe, are intended to replace os.system.

davidism
  • 121,510
  • 29
  • 395
  • 339
7

If you only need to check if the ping was successful, look at the status code; ping returns 2 for a failed ping, 0 for a success.

I'd use subprocess.Popen() (and not subprocess.check_call() as that raises an exception when ping reports the host is down, complicating handling). Redirect stdout to a pipe so you can read it from Python:

ipaddress = '198.252.206.140'  # guess who
proc = subprocess.Popen(
    ['ping', '-c', '3', ipaddress],
    stdout=subprocess.PIPE)
stdout, stderr = proc.communicate()
if proc.returncode == 0:
    print('{} is UP'.format(ipaddress))
    print('ping output:')
    print(stdout.decode('ASCII'))

You can switch to subprocess.DEVNULL* if you want to ignore the output; use proc.wait() to wait for ping to exit; you can add -q to have ping do less work, as it'll produce less output with that switch:

proc = subprocess.Popen(
    ['ping', '-q', '-c', '3', ipaddress],
    stdout=subprocess.DEVNULL)
proc.wait()
if proc.returncode == 0:
    print('{} is UP'.format(ipaddress))

In both cases, proc.returncode can tell you more about why the ping failed, depending on your ping implementation. See man ping for details. On OS X the manpage states:

EXIT STATUS
     The ping utility exits with one of the following values:

     0       At least one response was heard from the specified host.

     2       The transmission was successful but no responses were received.

     any other value
             An error occurred.  These values are defined in <sysexits.h>.

and man sysexits lists further error codes.

The latter form (ignoring the output) can be simplified by using subprocess.call(), which combines the proc.wait() with a proc.returncode return:

status = subprocess.call(
    ['ping', '-q', '-c', '3', ipaddress],
    stdout=subprocess.DEVNULL)
if status == 0:
    print('{} is UP'.format(ipaddress))

* subprocess.DEVNULL is new in Python 3.3; use open(os.devnull, 'wb') in it's place in older Python versions, making use of the os.devnull value, e.g.:

status = subprocess.call(
    ['ping', '-q', '-c', '3', ipaddress],
    stdout=open(os.devnull, 'wb'))
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • How can we add a "time limit" for this test?. For example, if the ping request "hangs" => then there are several cases you can wait forever. can we limit the test for (for example) 10 seconds and then check output? – Kosem Feb 08 '18 at 15:33
  • @Kosem: The `subprocess` module supports timeouts. In the above example, you could use `proc.wait(timeout=)`, or pass the same argument to the `subprocess.call()` examples. – Martijn Pieters Feb 08 '18 at 15:45
  • @sorh without version information and errors I can’t help you make things work. I've retested all the examples in my answer on Python 3.8 and they all still work for me. – Martijn Pieters Apr 30 '20 at 23:32
  • eventually I manage to run it, the '-c' switch wasn't working for some reason, however this wasn't good for me either since the cmd window pops up briefly every time it runs.. – sorh May 02 '20 at 20:47
  • @sorh: `-c` is the 'count' option. If you are using Windows, check the documentation for the ping command and see what command-line options are supported. [This documentation](https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/ping) suggests you need to use `/n` (or `-n`) instead of `-c`, and there doesn't appear an equivalent switch for `-q`. However, that seems to apply to Windows Server only. – Martijn Pieters May 03 '20 at 15:01
  • 2021, still works fine for me on newest Python: `def PingDevice(device):` `return(device) if subprocess.call(["ping","-n","2","-w","500",str(device)], stdout=subprocess.DEVNULL) == 0 else None` – Cow Nov 23 '21 at 09:49