10

So, I'm trying to get similar results using python as I do with a bash script.

Code for the bash script:

    #!/bin/bash

    for ip in $(seq 1 254); do
        ping -c 1 10.10.10.$ip | grep "bytes from" | cut -d " " -f 4 | cut -d ":" -f 1 &
    done

The thing that I would like to do is get the same results with similar speed. The issue that I've had with every version of the python script is that it takes a very long time to complete compared to the few seconds the batch script takes.

The batch file takes about 2 seconds to sweep a /24 network while the the best I can get with the python script is about 5-8 minutes.

Latest version of python script:

import subprocess

cmdping = "ping -c1 10.10.10."

for x in range (2,255):
    p = subprocess.Popen(cmdping+str(x), shell=True, stderr=subprocess.PIPE)

    while True:
        out = p.stderr.read(1)
        if out == '' and p.poll() != None:
            break
        if out != '':
            sys.stdout.write(out)
            sys.stdout.flush()

I've tried several different ways in python but can't get anywhere near the speed of the bash script.

Any suggestions?

Thomas Dignan
  • 7,052
  • 3
  • 40
  • 48
digital_alchemy
  • 663
  • 4
  • 9
  • 19
  • Can you be more specific - how long is "very long time" compared to "few seconds"? – Greg Hewgill Jan 20 '14 at 02:46
  • 4
    That ampersand at the end of the bash script causes the processes to run in the background. Your Python script runs them all one-by-one. – Blender Jan 20 '14 at 02:48
  • The batch file takes about 2 seconds to sweep a /24 network while the the best I can get with the python script is about 5-8 minutes. – digital_alchemy Jan 20 '14 at 02:48
  • 2
    @Blender: Hah! I had missed that `&`. Yes, that is obviously a key point. – Greg Hewgill Jan 20 '14 at 02:50
  • here's [code that pings all ips at once](http://stackoverflow.com/a/12102040/4279) (no need for `multiprocessing` here). Here's a [code example that limits number of concurrent pings](http://stackoverflow.com/a/26321632/4279) – jfs Oct 12 '14 at 04:15

1 Answers1

21

Multiprocessing

#!/usr/bin/python2

import multiprocessing
import subprocess
import os

def pinger( job_q, results_q ):
    DEVNULL = open(os.devnull,'w')
    while True:
        ip = job_q.get()
        if ip is None: break

        try:
            subprocess.check_call(['ping','-c1',ip],
                                  stdout=DEVNULL)
            results_q.put(ip)
        except:
            pass

if __name__ == '__main__':
    pool_size = 255

    jobs = multiprocessing.Queue()
    results = multiprocessing.Queue()

    pool = [ multiprocessing.Process(target=pinger, args=(jobs,results))
             for i in range(pool_size) ]

    for p in pool:
        p.start()

    for i in range(1,255):
        jobs.put('192.168.1.{0}'.format(i))

    for p in pool:
        jobs.put(None)

    for p in pool:
        p.join()

    while not results.empty():
        ip = results.get()
        print(ip)
mojo
  • 4,050
  • 17
  • 24
  • What's the purpose of `jobs.put(None)`? @mojo – Isaias Oct 03 '14 at 04:28
  • @Isaias Each None is a signal that there are no more jobs. You could perhaps end() the queue and get similar results if you modified the worker process code. – mojo Oct 07 '14 at 13:32