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
Linux/Gcc: Ldd Functionality from Inside a C/C++ Program
How to Install Apxs Module on Apache 2.4.6
Linux - Bash - Get $Releasever and $Basearch Values
Difference Between Virtual Page and Page Frame
Minimizing Copies When Writing Large Data to a Socket
Understanding Tcpdump Filter & Bit-Masking
What Is The Significance of This_Module in Linux Kernel Module Drivers
Manage Source Under Git and Svn Simultanously - Does It Make Sense
Difference Between Tcp_Max_Syn_Backlog and Somaxconn
Multiplication with Expr in Shell Script
How to Run Script Commands from Variables
Sed Replacement Not Working When Using Variables
How to Call Accept() for One Socket from Several Threads Simultaneously