How can I write a Linux bash script that tells me which computers are ON in my LAN?

前端 未结 16 1590
攒了一身酷
攒了一身酷 2020-12-12 11:59

How can I write a Linux Bash script that tells me which computers are ON in my LAN?

It would help if I could give it a range of IP addresses as input.

16条回答
  •  谎友^
    谎友^ (楼主)
    2020-12-12 12:45

    I would suggest using nmap's ping-scan flag,

    $ nmap -sn 192.168.1.60-70
    
    Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2009-04-09 20:13 BST
    Host machine1.home (192.168.1.64) appears to be up.
    Host machine2.home (192.168.1.65) appears to be up.
    Nmap finished: 11 IP addresses (2 hosts up) scanned in 0.235 seconds
    

    That said, if you want to write it yourself (which is fair enough), this is how I would do it:

    for ip in 192.168.1.{1..10}; do ping -c 1 -t 1 $ip > /dev/null && echo "${ip} is up"; done
    

    ..and an explanation of each bit of the above command:

    Generating list of IP addresses

    You can use the {1..10} syntax to generate a list of numbers, for example..

    $ echo {1..10}
    1 2 3 4 5 6 7 8 9 10
    

    (it's also useful for things like mkdir {dir1,dir2}/{sub1,sub2} - which makes dir1 and dir2, each containing sub1 and sub2)

    So, to generate a list of IP's, we'd do something like

    $ echo 192.168.1.{1..10}
    192.168.1.1 192.168.1.2 [...] 192.168.1.10
    

    Loops

    To loop over something in bash, you use for:

    $ for thingy in 1 2 3; do echo $thingy; done
    1
    2
    3
    

    Pinging

    Next, to ping.. The ping command varies a bit with different operating-systems, different distributions/versions (I'm using OS X currently)

    By default (again, on the OS X version of ping) it will ping until interrupted, which isn't going to work for this, so ping -c 1 will only try sending one packet, which should be enough to determine if a machine is up.

    Another problem is the timeout value, which seems to be 11 seconds on this version of ping.. It's changed using the -t flag. One second should be enough to see if a machine on the local network is alive or not.

    So, the ping command we'll use is..

    $ ping -c 1 -t 1 192.168.1.1
    PING 192.168.1.1 (192.168.1.1): 56 data bytes
    
    --- 192.168.1.1 ping statistics ---
    1 packets transmitted, 0 packets received, 100% packet loss
    

    Checking ping result

    Next, we need to know if the machine replied or not..

    We can use the && operator to run a command if the first succeeds, for example:

    $ echo && echo "It works"
    
    It works
    $ nonexistantcommand && echo "This should not echo"
    -bash: nonexistantcommand: command not found
    

    Good, so we can do..

    ping -c 1 -t 1 192.168.1.1 && echo "192.168.1.1 is up!"

    The other way would be to use the exit code from ping.. The ping command will exit with exit-code 0 (success) if it worked, and a non-zero code if it failed. In bash you get the last commands exit code with the variable $?

    So, to check if the command worked, we'd do..

    ping -c 1 -t 1 192.168.1.1;
    if [ $? -eq 0 ]; then
        echo "192.168.1.1 is up";
    else 
        echo "ip is down";
    fi
    

    Hiding ping output

    Last thing, we don't need to see the ping output, so we can redirect stdout to /dev/null with the > redirection, for example:

    $ ping -c 1 -t 1 192.168.1.1 > /dev/null && echo "IP is up"
    IP is up
    

    And to redirect stderr (to discard the ping: sendto: Host is down messages), you use 2> - for example:

    $ errorcausingcommand
    -bash: errorcausingcommand: command not found
    $ errorcausingcommand 2> /dev/null
    $
    

    The script

    So, to combine all that..

    for ip in 192.168.1.{1..10}; do  # for loop and the {} operator
        ping -c 1 -t 1 192.168.1.1 > /dev/null 2> /dev/null  # ping and discard output
        if [ $? -eq 0 ]; then  # check the exit code
            echo "${ip} is up" # display the output
            # you could send this to a log file by using the >>pinglog.txt redirect
        else
            echo "${ip} is down"
        fi
    done
    

    Or, using the && method, in a one-liner:

    for ip in 192.168.1.{1..10}; do ping -c 1 -t 1 $ip > /dev/null && echo "${ip} is up"; done
    

    Problem

    It's slow.. Each ping command takes about 1 second (since we set the -t timeout flag to 1 second). It can only run one ping command at a time.. The obvious way around this is to use threads, so you can run concurrent commands, but that's beyond what you should use bash for..

    "Python threads - a first example" explains how to use the Python threading module to write a multi-threaded ping'er.. Although at that point, I would once again suggest using nmap -sn..

提交回复
热议问题