Get target IP address of an iscsi device - linux

Suppose I have an iSCSI device /dev/sdat, how do I know the IP address of it's target?
The target driver is SCST, and the initiator is iSCSI. All I know is a device named /dev/sdat and nothing more. So how to get the IP address of it's target?

Well, I'm not proud of this, but it gets the job done. At least for some definitions of getting the job done.
The basic idea is this. You can get the target IQN from the output of lsscsi -t. (You'll need the lsscsi program if you don't already have it. I think you'll find it's essential in any kind of SCSI environment.)
# lsscsi -t
[2:0:0:0] disk iqn.2009-12.com.blockbridge:t-pjxfzufjkp-illoghjk,t,0x1 /dev/sda
[3:0:0:0] disk iqn.2009-12.com.blockbridge:t-pjxfzuecga-eajejghg,t,0x1 /dev/sdb
[4:0:0:0] disk iqn.2009-12.com.blockbridge:t-pjxfzufjjo-pokqaja,t,0x1 /dev/sdd
[5:0:0:0] disk iqn.2009-12.com.blockbridge:t-pjxfzufnfg-cqikkgl,t,0x1 /dev/sdc
Then, you can feed the target IQN into iscsiadm and grep around in the output for the target address.
# iscsiadm -m node -T iqn.2009-12.com.blockbridge:t-pjxfzufjkp-illoghjk | egrep 'node.conn.+address'
node.conn[0].address = 172.16.5.148
Putting it all together, you get a script like this. Of course, this is absent all kinds of error handling, and probably doesn't handle about 23 different cases. But, hey... It works in my environment!
#!/usr/bin/bash
if [[ -z $1 ]]; then
>&2 echo "Usage: devip.sh <device>"
exit 1
fi
iqn=$(sudo lsscsi -t | grep "$1" | grep iqn | awk '{print $3}' | awk -F , '{print $1}')
if [[ -z "$iqn" ]]; then
>&2 echo "IQN not found for \"$1\"."
exit 1
fi
sudo iscsiadm -m node -T $iqn | egrep 'node.conn.+address' | awk -F ' *= *' '{print $2}'
exit $?

Related

loop to test all NFS mount point

this code work very well
mountpoint="/mnt/testnfs"
read -t1 < <(stat -t "$mountpoint" 2>&-)
if [ -z "$REPLY" ] ; then
echo "NFS mount stale. Removing..."
fi
If I try to put it into a loop for :
declare -a nfs_array=( "/mnt/testnfs1" "/mnt/testnfs2/" )
for i in "${nfs_array[#]}"
do
read -t1 < <(stat -t "$nfs_array" 2>&-)
if [ -z "$REPLY" ] ; then
echo "NFS dead"
fi
done
Aim is to test all mounts points, this code test and read only the first entries from nfs_array. If I swapped testnfs1 with testnfs2 this code will test testnfs2 mount point and forget testnfs1 :-(
In your loop it should be:
read -r -t1 < <(stat -t "$i" 2>&-)
Currently it's just reading the first array value and $i isn't used.
If you really want to list all nfs mounts (the title tells so), then use either:
mount | grep ' type nfs' | ...
This may have false positives because a mount point or mounted path contains type nfs.
If the /proc/ file system is available, this is a better way:
awk '$3 ~ /^nfs/ {print}' /proc/mounts | ...
Here I'm not sure what happens, if mount point or mounted path contains a space -- I never had this situation.

Run Shell Script without Command line Arguments in Linux

I am trying to run a Shell Script, but got stuck on an issue. I want to Run certain set of code when i supply arguments and remaining should run, if i dont pass any argument.
Part which i want to run with args:
#!/bin/bash
while [[ "$1" != "" ]]; do
case "$1" in
-c ) cat /proc/cpuinfo | grep cores
;;
-d ) fdisk -l | grep Disk | awk '{print $1,$2,$3,$4}' #fdisk -l 2> /dev/null | grep Disk | grep -v identifier
;;
esac
shift
done
and this part without any args
while [[ $# -eq 0 ]]; do
echo PART 2 $#
cat /proc/cpuinfo | grep cores
fdisk -l | grep Disk | awk '{print $1,$2,$3,$4}' #fdisk -l 2> /dev/null | grep Disk | grep -v identifier
break
done
I believe issue is with the Loop condition, but i cant understand what?
if [[ -n "$1" ]]; then
#
# "$1" is not empty. This is the part which runs when one or more
# arguments are supplied.
#
while [[ -n "$1" ]]; do
case "$1" in
-c) cat /proc/cpuinfo | grep cores
;;
-d) LC_ALL=C fdisk -l | grep Disk | awk '{print $1,$2,$3,$4}'
#LC_ALL=C fdisk -l 2> /dev/null | grep Disk | grep -v identifier
;;
esac
shift
done
exit
fi
#
# "$1" is empty. The following code runs when no arguments are supplied.
#
echo PART 2 $#
cat /proc/cpuinfo | grep cores
LC_ALL=C fdisk -l | grep Disk | awk '{print $1,$2,$3,$4}'
#LC_ALL=C fdisk -l 2> /dev/null | grep Disk | grep -v identifier
Note 1: Not tested.
Note 2: Whenever you feel a need to parse the output of a command looking for certain words or phrases it is a good idea to run the command in the default locale by prefixing it with LC_ALL=C. In this way you won't be surprised when in a French locale, for example, fdisk says Disque...

Bash script to find filesystem usage

EDIT: Working script below
I have used this site MANY times to get answers, but I am a little stumped with this.
I am tasked with writing a script, in bash, to log into roughly 2000 Unix servers (Solaris, AIX, Linux) and check the size of OS filesystems, most notable /var /usr /opt.
I have set some variables, which may be where I am going wrong right off the bat.
1.) First I am connecting to another server that has a list of all hosts in the infrastructure. Then I parse this data with some sed commands to get a list I can use properly
1.) Then I do a ping test, to see if the server is alive. If the server is decom. The idea behind this, is if the server is not pingable, I don't want it being reported on, or any attempt to be made to connect to it, as it is just wasting time. I feel I am doing this wrong, but don't know how to do it corectly (a re-occurring theme you will here in this post lol)
If any FS is over 80% mark, then it should output to a text file with the servername, filesystem, size on one line <== very important for me
If the FS is under 80% full, then I don't want it in my output, it can me omitted completely.
I have created something that I will post below, and am hoping to get some help in figuring out where I am going wrong. I am very new to bash scripting, but have experience as a Unix admin (i have never been good at scripting).
Can anyone provide some direction and teach me where I am going wrong?
I will upload my script that i can confirm is working hopefully tomorrow. thanks everyone for your input in this!
Here is my "disk usage" linux script, i hope that help you.
#!/bin/sh
df -H | awk '{ print $5 " " $6 }' | while read output;
do
echo $output
usep=$(echo $output | awk '{ print $1}' | cut -d'%' -f1 )
partition=$(echo $output | awk '{ print $2 }' )
if [ $usep -ge 90 ]; then
echo "Running out of space \"$partition ($usep%)\" on $(hostname) as on $(date)" |
mail -s "Warning! There is no space on the disk: $usep%" root#domain.com
fi
done
Some trouble is here:
ping -c 1 -W 3 $i > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "$i is offline" >> $LOG
fi
You need a continue statement inside that if. Your program isn't really treating non-pingable hosts differently, just logging they're not pingable.
Okay, now I'm looking a little deeper, and there's more naive stuff in here. These shouldn't work:
SOLVARFS=$(df -h /var |cut -f5 |grep -v capacity |awk '{print $5}')
SOLUSRFS=$(df -h /usr |cut -f5 |grep -v capacity |awk '{print $5}')
SOLOPTFS=$(df -h /opt |cut -f5 |grep -v capacity |awk '{print $5}')
etc...
The problem with these lines is, the command substitution gets assigned to the variables before the ssh session happens. So the content of each variable is the command's result on your local system, not the command itself. Since you're doing command substitution around your ssh calls, it might well work just to rewrite these lines as (note the backslash escapes on $5):
SOLVARFS="df -h /var |cut -f5 |grep -v capacity |awk '{print \$5}'"
SOLUSRFS="df -h /usr |cut -f5 |grep -v capacity |awk '{print \$5}'"
SOLOPTFS="df -h /opt |cut -f5 |grep -v capacity |awk '{print \$5}'"
etc...
The part where you're contacting another server has some more stuff to correct. You don't need three if statements per server, and there's no reason to echo anything to /dev/null. Here's a rewrite for the SunOS section. For each directory you're checking, it outputs the host name, the command name (so you can see which dir was being checked), and the result:
if [[ $UNAME = "SunOS" ]]; then
for SSH_COMMAND in SOLVARFS SOLUSRFS SOLOPTFS ; do
RESULT=`ssh -o PasswordAuthentication=no -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=2 GSSAPIAuthentication=no -q $i ${!SSH_COMMAND}`
if ["$RESULT" -gt 80] ; do
echo "$i, $SSH_COMMAND, $RESULT" >> $LOG
fi
done
fi
Note that the ${!BLAH} construction is variable indirection. "Give me the contents of the variable named by BLAH".
Your original script does a bunch of things less-than-optimally. Rather than running an almost-identical block of code for each filesystem and each operating system, the thing to do would be to record the differences in a way that a SINGLE piece of code can iterate over all your objects, adapting as required.
Here's my take on this. Commands should appear ONCE, but
they get run multiple times by loops, and
they get run multiple ways using arrays.
The following script passes lint checks, but obviously this is untested, as I don't have your environment to test in.
You might still want to think about how your logging and notifications work.
#!/bin/bash
# Assign temp file, remove it automatically upon successful exit.
tmpfile=$(mktemp /tmp/${0##*/}.XXXX)
trap "rm '$tmpfile'" 0
#NOW=$(date +"%Y-%m-%d-%T")
NOW=$(date +"%F")
LOG=/usr/scripts/disk_usage/Unix_df_issues-$NOW.txt
printf '' > "$LOG"
# Use variables to refer to commonly accessed files. If you change a name, just do it once.
rawhostlist=all_vms.txt
host_os=${rawhostlist}_OS
# Commonly-used options need only be declared once. Use an array for easier management.
declare -a ssh_opts=()
ssh_opts+=(-o PasswordAuthentication=no)
ssh_opts+=(-o BatchMode=yes)
ssh_opts+=(-o StrictHostKeyChecking=no) # Eliminate prompts on new hosts
ssh_opts+=(-o ConnectTimeout=2) # This should make your `ping` unnecessary.
ssh_opts+=(-o GSSAPIAuthentication=no) # This is default. Do we really need it?
# Note: Associative arrays require Bash 4.x.
declare -A df_opts=(
[SunOS]="-h"
[Linux]="-hP"
[AIX]=""
)
declare -A df_column=(
[SunOS]=5
[Linux]=5
[AIX]=4
)
# Fetch host list from configserver, stripping /^adm/ on the remote end.
ssh "${ssh_opts[#]}" -q configserver "sed 's/^adm//' /reports/*/HOSTNAME" > "$rawhostlist"
# Confirm that our host_os cache is up to date and process any missing hosts.
awk '
NR==FNR { h[$1]; next } # Add everything in rawhostlist to an array...
{ delete h[$1] } # Then remove any entries that exist in host_os.
END {
for (i in h) print i # And print whatever remains.
}' "$rawhostlist" "$host_os" |
while read h; do
printf '%s\t%s\n' "$h" $(ssh "$h" "${ssh_opts[#]}" -q uname -s)
done >> "$host_os"
# Next, step through the host list and collect data.
while read host os; do
ssh "${ssh_opts[#]}" "$host" df "${df_opts[$os]}" /var /usr /opt |
awk -v column="${df_column[$os]}" -v host="$host" 'NR>1 { print host,$1,$column }'
)
done < "$host_os" > "$tmpfile"
# Now that we have all our data, check for warning/critical levels.
while read host filesystem usage; do
if [ "$usage" -gt 80 ]; then
status="CRITICAL"
elif [ "$usage" -gt 70 ]; then
status="WARNING"
else
continue
fi
# Log our results to our log file, AND send them to stderr.
printf "[%s] %s: %s:%s at %d%%\n" "$(date +"%F %T")" "$status" "$host" "$filesystem" "$usage" | tee -a "$LOG" >&2
done < "$tmpfile"
# Email and record our results.
if [ -s "$LOG" ]; then
mail -s "Daily Unix /var Report - $NOW" unixsystems#examplle.com < "$LOG"
mv "$LOG" /var/log/vm_reports/
fi
Consider this example code. If you like the way it looks, your next task is to debug it, or open new questions for parts that you're having trouble debugging. :-)

Bash monitor disk usage

I bought a NAS box which has a cut down version of debian on it.
It ran out of space the other day and I did not realise. I am basically wanting to write a bash script that will alert me whenever the disk gets over 90% full.
Is anyone aware of a script that will do this or give me some advice on writing one?
#!/bin/bash
source /etc/profile
# Device to check
devname="/dev/sdb1"
let p=`df -k $devname | grep -v ^File | awk '{printf ("%i",$3*100 / $2); }'`
if [ $p -ge 90 ]
then
df -h $devname | mail -s "Low on space" my#email.com
fi
Crontab this to run however often you want an alert
EDIT: For multiple disks
#!/bin/bash
source /etc/profile
# Devices to check
devnames="/dev/sdb1 /dev/sda1"
for devname in $devnames
do
let p=`df -k $devname | grep -v ^File | awk '{printf ("%i",$3*100 / $2); }'`
if [ $p -ge 90 ]
then
df -h $devname | mail -s "$devname is low on space" my#email.com
fi
done
I tried to use Erik's answer but had issues with devices having long names which wraps the numbers and causes script to fail, also the math looked wrong to me and didn't match the percentages reported by df itself.
Here's an update to his script:
#!/bin/bash
source /etc/profile
# Devices to check
devnames="/dev/sda1 /dev/md1 /dev/mapper/vg1-mysqldisk1 /dev/mapper/vg4-ctsshare1 /dev/mapper/vg2-jbossdisk1 /dev/mapper/vg5-ctsarchive1 /dev/mapper/vg3-muledisk1"
for devname in $devnames
do
let p=`df -Pk $devname | grep -v ^File | awk '{printf ("%i", $5) }'`
if [ $p -ge 70 ]
then
df -h $devname | mail -s "$devname is low on space" my#email.com
fi
done
Key changes are changed df -k to df -Pk to avoid line wrapping and simplified the awk to use pre-calc'd percent instead of recalcing.
You could also use Monit for this kind of job. It's a "free open source utility for managing and monitoring, processes, programs, files, directories and filesystems on a UNIX system".
Based on #Erik answer, here is my version with variables :
#!/bin/bash
DEVNAMES="/ /home"
THRESHOLD=80
EMAIL=you#email.com
host=$(hostname)
for devname in $DEVNAMES
do
current=$(df $devname | grep / | awk '{ print $5}' | sed 's/%//g')
if [ "$current" -gt "$THRESHOLD" ] ; then
mail -s "Disk space alert on $host" "$EMAIL" << EOF
WARNING: partition $devname on $host is $current% !!
To list big files (>100Mo) :
find $devname -xdev -type f -size +100M
EOF
fi
done
And if you do not have the mail command on your server, you can send email via SMPT with swaks :
swaks --from "$EMAIL" --to "$EMAIL" --server "TheServer" --auth LOGIN --auth-user "TheUser" --auth-password "ThePasswrd" --h-Subject "Disk space alert on $host" --body - << EOF
#!/bin/bash
DEVNAMES=$(df --output=source | grep ^/dev)
THRESHOLD=90
EMAIL=your#email
HOST=$(hostname)
for devname in $DEVNAMES
do
current=$(df $devname | awk 'NR>1 {printf "%i",$5}')
[ "$current" -gt "$THRESHOLD" ] && warn="WARNING: partition $devname on $HOST is $current% !! \n$warn"
done
[ "$warn" ] && echo -e "$warn" | mail -s "Disk space alert on $HOST" $EMAIL
Based on previous answers, here's my version with following changes:
Automatically checks all mounted devices
Sends only one mail per check, regardless of how many devices are over the threshold
Code generally tidied up

How can I 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?
It would help if I could give it a range of IP addresses as input.
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..
In the real world, you could use nmap to get what you want.
nmap -sn 10.1.1.1-255
This will ping all the addresses in the range 10.1.1.1 to 10.1.1.255 and let you know which ones answer.
Of course, if you in fact want to do this as a bash exercise, you could run ping for each address and parse the output, but that's a whole other story.
Assuming my network is 10.10.0.0/24, if i run a ping on the broadcast address like
ping -b 10.10.0.255
I'll get an answer from all computers on this network that did not block their ICMP ping port.
64 bytes from 10.10.0.6: icmp_seq=1 ttl=64 time=0.000 ms
64 bytes from 10.10.0.12: icmp_seq=1 ttl=64 time=0.000 ms
64 bytes from 10.10.0.71: icmp_seq=1 ttl=255 time=0.000 ms
So you just have to extract the 4th column, with awk for example:
ping -b 10.10.0.255 | grep 'bytes from' | awk '{ print $4 }'
10.10.0.12:
10.10.0.6:
10.10.0.71:
10.10.0.95:
Well, you will get duplicate, and you may need to remove the ':'.
EDIT from comments :
the -c option limits the number of pings
since the script will end, we can also limit ourself on unique IPs
ping -c 5 -b 10.10.0.255 | grep 'bytes from' | awk '{ print $4 }' | sort | uniq
There is also fping:
fping -g 192.168.1.0/24
or:
fping -g 192.168.1.0 192.168.1.255
or show only hosts that are alive:
fping -ag 192.168.1.0/24
It pings hosts in parallel so the scan is very fast. I don't know a distribution which includes fping in its default installation but in most distributions you can get it through the package manager.
Also using the "ping the broadcast address" method pointed out by chburd, this pipe should do the trick for you:
ping -c 5 -b 10.11.255.255 | sed -n 's/.* \([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | sort | uniq
Of course, you'd have to change the broadcast address to that of your network.
Just for fun, here's an alternate
#!/bin/bash
nmap -sP 192.168.1.0/24 > /dev/null 2>&1 && arp -an | grep -v incomplete | awk '{print$2}' | sed -e s,\(,, | sed -e s,\),,
If you're limiting yourself to only having the last octet changing, this script should do it. It should be fairly obvious how to extend it from one to multiple octets.
#! /bin/bash
BASE=$1
START=$2
END=$3
counter=$START
while [ $counter -le $END ]
do
ip=$BASE.$counter
if ping -qc 2 $ip
then
echo "$ip responds"
fi
counter=$(( $counter + 1 ))
done
ip neighbor
arp -a
Arpwatch
As other posters pointed out, nmap is the way to go, but here's how to do the equivalent of a ping scan in bash. I wouldn't use the broadcast ping, as a lot of systems are configured not to respond to broadcast ICMP nowadays.
for i in $(seq 1 254); do
host="192.168.100.$i"
ping -c 1 -W 1 $host &> /dev/null
echo -n "Host $host is "
test $? -eq 0 && echo "up" || echo "down"
done
#!/bin/bash
#Get the ip address for the range
ip=$(/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}' | cut -d"." -f1,2,3)
# ping test and list the hosts and echo the info
for range in $ip ; do [ $? -eq 0 ] && ping -c 1 -w 1 $range > /dev/null 2> /dev/null && echo "Node $range is up"
done
Although an old question, it still seems to be important (at least important enough for me to deal with this). My script relies on nmap too, so nothing special here except that ou can define which interface you want to scan and the IP Range is created automagically (at least kind of).
This is what I came up with
#!/bin/bash
#Script for scanning the (local) network for other computers
command -v nmap >/dev/null 2>&1 || { echo "I require nmap but it's not installed. Aborting." >&2; exit 1; }
if [ -n ""$#"" ]; then
ip=$(/sbin/ifconfig $1 | grep 'inet ' | awk '{ print $2}' | cut -d"." -f1,2,3 )
nmap -sP $ip.1-255
else
echo -e "\nThis is a script for scanning the (local) network for other computers.\n"
echo "Enter Interface as parameter like this:"
echo -e "\t./scannetwork.sh $(ifconfig -lu | awk '{print $2}')\n"
echo "Possible interfaces which are up are: "
for i in $(ifconfig -lu)
do
echo -e "\033[32m \t $i \033[39;49m"
done
echo "Interfaces which could be used but are down at the moment: "
for i in $(ifconfig -ld)
do
echo -e "\033[31m \t $i \033[39;49m"
done
echo
fi
One remark: This script is created on OSX, so there might be some changes to linux environments.
If you want to provide a list of hosts it can be done with nmap, grep and awk.
Install nmap:
$ sudo apt-get install nmap
Create file hostcheck.sh like this:
hostcheck.sh
#!/bin/bash
nmap -sP -iL hostlist -oG pingscan > /dev/null
grep Up pingscan | awk '{print $2}' > uplist
grep Down pingscan | awk '{print $2}' > downlist
-sP: Ping Scan - go no further than determining if host is online
-iL : Input from list of hosts/networks
-oG : Output scan results in Grepable format, to the given filename.
/dev/null : Discards output
Change the access permission:
$ chmod 775 hostcheck.sh
Create file hostlist with the list of hosts to be checked (hostname or IP):
hostlist (Example)
192.168.1.1-5
192.168.1.101
192.168.1.123
192.168.1.1-5 is a range of IPs
Run the script:
./hostcheck.sh hostfile
Will be generated files pingscan with all the information, uplist with the hosts online (Up) and downlist with the hosts offline (Down).
uplist (Example)
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.101
downlist (Example)
192.168.1.5
192.168.1.123
Some machines don't answer pings (e.g. firewalls).
If you only want the local network you can use this command:
(for n in $(seq 1 254);do sudo arping -c1 10.0.0.$n & done ; wait) | grep reply | grep --color -E '([0-9]+\.){3}[0-9]+'
Explanations part !
arping is a command that sends ARP requests. It is present on most of linux.
Example:
sudo arping -c1 10.0.0.14
the sudo is not necessary if you are root ofc.
10.0.0.14 : the ip you want to test
-c1 : send only one request.
&: the 'I-don't-want-to-wait' character
This is a really useful character that give you the possibility to launch a command in a sub-process without waiting him to finish (like a thread)
the for loop is here to arping all 255 ip addresses. It uses the seq command to list all numbers.
wait: after we launched our requests we want to see if there are some replies. To do so we just put wait after the loop.
wait looks like the function join() in other languages.
(): parenthesis are here to interpret all outputs as text so we can give it to grep
grep: we only want to see replies. the second grep is just here to highlight IPs.
hth
Edit 20150417: Maxi Update !
The bad part of my solution is that it print all results at the end. It is because grep have a big enough buffer to put some lines inside.
the solution is to add --line-buffered to the first grep.
like so:
(for n in $(seq 1 254);do sudo arping -c1 10.0.0.$n & done ; wait) | grep --line-buffered reply | grep --color -E '([0-9]+\.){3}[0-9]+'
#!/bin/bash
for ((n=0 ; n < 30 ; n+=1))
do
ip=10.1.1.$n
if ping -c 1 -w 1 $ip > /dev/null 2> /dev/null >> /etc/logping.txt; then
echo "${ip} is up" # output up
# sintax >> /etc/logping.txt log with .txt format
else
echo "${ip} is down" # output down
fi
done
The following (evil) code runs more than TWICE as fast as the nmap method
for i in {1..254} ;do (ping 192.168.1.$i -c 1 -w 5 >/dev/null && echo "192.168.1.$i" &) ;done
takes around 10 seconds, where the standard nmap
nmap -sP 192.168.1.1-254
takes 25 seconds...
Well, this is part of a script of mine.
ship.sh 🚢 A simple, handy network addressing 🔎 multitool with plenty of features 🌊
Pings network, displays online hosts on that network with their local IP and MAC address
It doesn't require any edit. Needs root permission to run.
GOOGLE_DNS="8.8.8.8"
ONLINE_INTERFACE=$(ip route get "${GOOGLE_DNS}" | awk -F 'dev ' 'NR == 1 {split($2, a, " "); print a[1]}')
NETWORK_IP=$(ip route | awk "/${ONLINE_INTERFACE}/ && /src/ {print \$1}" | cut --fields=1 --delimiter="/")
NETWORK_IP_CIDR=$(ip route | awk "/${ONLINE_INTERFACE}/ && /src/ {print \$1}")
FILTERED_IP=$(echo "${NETWORK_IP}" | awk 'BEGIN{FS=OFS="."} NF--')
ip -statistics neighbour flush all &>/dev/null
echo -ne "Pinging ${NETWORK_IP_CIDR}, please wait ..."
for HOST in {1..254}; do
ping "${FILTERED_IP}.${HOST}" -c 1 -w 10 &>/dev/null &
done
for JOB in $(jobs -p); do wait "${JOB}"; done
ip neighbour | \
awk 'tolower($0) ~ /reachable|stale|delay|probe/{printf ("%5s\t%s\n", $1, $5)}' | \
sort --version-sort --unique

Resources