How to Write a Linux Bash Script That Tells Me Which Computers Are on in My Lan

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

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

(ba)sh - Determine (based on machine name) whether a computer is on the local network

You can use nslookup (http://linux.die.net/man/1/nslookup), dig (http://linux.die.net/man/1/dig) or host (http://linux.die.net/man/1/host) command-line utilities.

For example, here is the result of running host for getting A-records for stackoverflow.com from DNS server:

$ host -tA stackoverflow.com
stackoverflow.com has address 69.59.197.21

Parallel Iterating IP Addresses in Bash

Small Scale - iterate

for a smaller IP address span it would probably be recommended to iterate like this:

for ip in 192.168.1.{1..10}; do ...

As stated in this similar question.

Big Scale - parallel !

Given that your problem deals with a huge IP address span you should probably consider a different approach.

This begs for the use of gnu parallel.

Parallel iterating a big span of IP addresses in bash using gnu parallel requires splitting the logic to several files (for the parallel command to use).

ip2int

#!/bin/bash

set -e

function ip_to_int()
{
local IP="$1"
local A=$(echo $IP | cut -d. -f1)
local B=$(echo $IP | cut -d. -f2)
local C=$(echo $IP | cut -d. -f3)
local D=$(echo $IP | cut -d. -f4)
local INT

INT=$(expr 256 "*" 256 "*" 256 "*" $A)
INT=$(expr 256 "*" 256 "*" $B + $INT)
INT=$(expr 256 "*" $C + $INT)
INT=$(expr $D + $INT)

echo $INT
}

function int_to_ip()
{
local INT="$1"

local D=$(expr $INT % 256)
local C=$(expr '(' $INT - $D ')' / 256 % 256)
local B=$(expr '(' $INT - $C - $D ')' / 65536 % 256)
local A=$(expr '(' $INT - $B - $C - $D ')' / 16777216 % 256)

echo "$A.$B.$C.$D"
}


scan_ip

#!/bin/bash

set -e

source ip2int

if [[ $# -ne 1 ]]; then
echo "Usage: $(basename "$0") ip_address_number"
exit 1
fi

CONNECT_TIMEOUT=2 # in seconds
IP_ADDRESS="$(int_to_ip ${1})"

set +e
data=$(curl --head -vs -m ${CONNECT_TIMEOUT} https://${IP_ADDRESS}:443 2>&1)
exit_code="$?"
data=$(echo -e "${data}" | grep "Server: ")
# wasn't sure what are you looking for in your servers
set -e

if [[ ${exit_code} -eq 0 ]]; then
if [[ -n "${data}" ]]; then
echo "${IP_ADDRESS} - ${data}"
else
echo "${IP_ADDRESS} - Got empty data for server!"
fi
else
echo "${IP_ADDRESS} - no server."
fi


scan_range

#!/bin/bash

set -e

source ip2int

START_ADDRESS="10.0.0.0"
NUM_OF_ADDRESSES="16777216" # 256 * 256 * 256

start_address_num=$(ip_to_int ${START_ADDRESS})
end_address_num=$(( start_address_num + NUM_OF_ADDRESSES ))

seq ${start_address_num} ${end_address_num} | parallel -P0 ./scan_ip

# This parallel call does the same as this:
#
# for ip_num in $(seq ${start_address_num} ${end_address_num}); do
# ./scan_ip ${ip_num}
# done
#
# only a LOT faster!


Improvement from the iterative approach:

The run time of the naive for loop (which is estimated to take 200 days for 256*256*256 addresses) was improved to under a day according to @skrskrskr.

How to detect when known wireless devices join my wireless LAN on Linux

You can use nmap to discover your network. Here you can find some examples.

Then you should parse it's output. E.g.:

while true; do
nmap -v -sT 192.168.0.0/24 | fgrep "YOUR_SEARCHED_IP" && \
echo BINGO "YOUR_SEARCHED_IP" IS IN THE 192.168.0.0/24 NETWORK
done

And nmap has an -sn option to skip the port checks.

Even better you can use ip neighbor show to see your neighborhood networks IP address.

Or you can use a simple ping test, like:

for ip in $(seq 1 254); do 
ping -c 1 192.168.1.$ip>/dev/null && \
echo “192.168.1.$ip is UP"
done

And you can combine it with nslookup to see the hostnames.

Get MAC address using shell script

Observe that the interface name and the MAC address are the first and last fields on a line with no leading whitespace.

If one of the indented lines contains inet addr: the latest interface name and MAC address should be printed.

ifconfig -a |
awk '/^[a-z]/ { iface=$1; mac=$NF; next }
/inet addr:/ { print iface, mac }'

Note that multiple interfaces could meet your criteria. Then, the script will print multiple lines. (You can add ; exit just before the final closing brace if you always only want to print the first match.)

How can I properly run a shell script automatically upon login with my mac?

You are trying to run commands that require root privileges. You can't do that using a launch agent that runs as your user.

You can do it using a login script. A login script runs as root, after you have authenticated. See “Customizing Login and Logout” to learn how to set up a login script.

How do I find my computer's IP address using the bash shell?

ifconfig en0 | grep inet | grep -v inet6

Output of above is expected to be in the following form:

inet 192.168.111.1 netmask 0xffffff00 broadcast 192.168.111.255

Add an awk statement to print the second column to avoid using cut (awk is a pretty standard unix tool):

ifconfig en0 | grep inet | grep -v inet6 | awk '{print $2}'

I use the following to get the current IP when on a LAN where the first few numbers of the IP are always the same (replace 192.168.111 with your own numbers):

ifconfig | grep 192.168.111 | awk '{print $2}'

To get the ip of another machine that you know the name of, try (replace hostname and 192.168.111 with your own values):

ping -c 1 hostname | grep 192.168.11 | grep 'bytes from' | awk '{print $4}' | sed 's/://g'


Related Topics



Leave a reply



Submit