Checking host availability by using ping in bash scripts
Asked Answered
D

11

99

I want to write a script, that would keep checking if any of the devices in network, that should be online all day long, are really online. I tried to use ping, but

if [ "`ping -c 1 some_ip_here`" ]
then
  echo 1
else
  echo 0
fi

gives 1 no matter if I enter valid or invalid ip address. How can I check if a specific address (or better any of devices from list of ip addresses) went offline?

Drinking answered 8/8, 2013 at 10:6 Comment(4)
You should consider using nmap, it allows you to specify IP address ranges.Waldman
FWIW, your snippet works fine for me.Kellikellia
Not an answer to the question, but you'd better use "$(ping -c 1 some_ip_here)" instead of "ping -c 1 some_ip_here". refer this link for more infoSulfatize
Nobody explained the actual problem with your script. What you wrote takes all the output printed by the ping command and then checks if that is a non-empty string. Since it always prints something, whether it succeeds or fails, it never prints an empty string. To check if a command is successful, don't surround it in backticks, quotes, or brackets. Just write if cmd; then ... fiCowcatcher
S
91

Ping returns different exit codes depending on the type of error.

ping 256.256.256.256 ; echo $?
# 68

ping -c 1 127.0.0.1 ; echo $?
# 0

ping -c 1 192.168.1.5 ; echo $?
# 2

0 means host reachable

2 means unreachable

Secularism answered 8/8, 2013 at 10:8 Comment(5)
cool... I'm still new to scripting and thought that if in my code does check the exit code...Drinking
It does, your if will trigger the echo 1 block on any non-0 exit code (all errors). But to figure out what kind of error it was you will to check the exact exit code.Secularism
Do you get 68 on the first? I get 2 and unknow host. I get 1 on third example with Destination Host Unreachable.Impeccable
I see the return code 68 on MacOS, but it is 2 on Linux for unresolvable hostname. There is also the -o option on MacOS (I assume in BSD also) which returns after a single successful packet.Gena
@Secularism no, the OP's command does not check for success/failure. It takes the text output printed by ping and checks if it is an empty string. Since ping always prints something for both success and failure, it never prints an empty string, so the script always does echo 1.Cowcatcher
M
93

You don't need the backticks in the if statement. You can use this check

if ping -c 1 some_ip_here &> /dev/null
then
  echo "success"
else
  echo "error"
fi

The if command checks the exit code of the following command (the ping). If the exit code is zero (which means that the command exited successfully) the then block will be executed. If it return a non-zero exit code, then the else block will be executed.

Mackinaw answered 8/8, 2013 at 10:15 Comment(4)
I suggest replacing "&>" with ">", or else the script will continue running.Salmagundi
@Salmagundi It depends on the shell. In bash &> file is equivalent to > file 2>&1, i.e. redirects both standard output and and standard error.Mackinaw
Can we also do ping -t 1 -c 1 <ip>?Supranatural
@alper: Sure, that will limit the TTL to a single hop.Mackinaw
S
91

Ping returns different exit codes depending on the type of error.

ping 256.256.256.256 ; echo $?
# 68

ping -c 1 127.0.0.1 ; echo $?
# 0

ping -c 1 192.168.1.5 ; echo $?
# 2

0 means host reachable

2 means unreachable

Secularism answered 8/8, 2013 at 10:8 Comment(5)
cool... I'm still new to scripting and thought that if in my code does check the exit code...Drinking
It does, your if will trigger the echo 1 block on any non-0 exit code (all errors). But to figure out what kind of error it was you will to check the exact exit code.Secularism
Do you get 68 on the first? I get 2 and unknow host. I get 1 on third example with Destination Host Unreachable.Impeccable
I see the return code 68 on MacOS, but it is 2 on Linux for unresolvable hostname. There is also the -o option on MacOS (I assume in BSD also) which returns after a single successful packet.Gena
@Secularism no, the OP's command does not check for success/failure. It takes the text output printed by ping and checks if it is an empty string. Since ping always prints something for both success and failure, it never prints an empty string, so the script always does echo 1.Cowcatcher
L
37

I can think of a one liner like this to run

ping -c 1 127.0.0.1 &> /dev/null && echo success || echo fail

Replace 127.0.0.1 with IP or hostname, replace echo commands with what needs to be done in either case.

Code above will succeed, maybe try with an IP or hostname you know that is not accessible.

Like this:

ping -c 1 google.com &> /dev/null && echo success || echo fail

and this

ping -c 1 lolcatz.ninja &> /dev/null && echo success || echo fail
Linares answered 15/8, 2015 at 22:2 Comment(1)
Very nice one liner!!Bibcock
S
25

There is advanced version of ping - "fping", which gives possibility to define the timeout in milliseconds.

#!/bin/bash
IP='192.168.1.1'
fping -c1 -t300 $IP 2>/dev/null 1>/dev/null
if [ "$?" = 0 ]
then
  echo "Host found"
else
  echo "Host not found"
fi
Spinneret answered 2/9, 2014 at 9:39 Comment(7)
ping also has a -t option that allows you to define a timeout.Luckless
True. But in seconds. fping - in milliseconds, thats important if You have lots of hosts to ping.Spinneret
In "unixy" ping -t isn't for the timeout, but for the TTL. The timeout is specified via -W. Note: this can still block for a long time, eg. if the DNS server goes away and a DNS name has to be resolved. With the recent attacks in mind this should be considered.Pun
Like many of the other answers here, this one has the antipattern cmd; if [ $? = 0 ]; then ... which is better and more idiomatically written if cmd; then ... -- the purpose of if and the other flow control statements in the shell is precisely to run a command and check its exit status. You should very rarely need to examine $? directly.Zachary
I would not say it's an anti-pattern, it's more question of personal choice. As for me, a command in standalone line is more readable.Spinneret
It is absolutely an anti-pattern.Indigotin
See shellcheck.net/wiki/SC2181 for why if cmd; then ... is preferred. Running your scripts through shellcheck is also recommended.Bladdernose
S
12

This is a complete bash script which pings target every 5 seconds and logs errors to a file.

Enjoy!

#!/bin/bash
        
        FILE=errors.txt
        TARGET=192.168.0.1

          touch $FILE
          while true;
          do
            DATE=$(date '+%d/%m/%Y %H:%M:%S')
            ping -c 1 $TARGET &> /dev/null
            if [[ $? -ne 0 ]]; then
              echo "ERROR "$DATE
              echo $DATE >> $FILE
            else
              echo "OK "$DATE
            fi
              sleep 5
          done
Shaddock answered 18/12, 2020 at 10:41 Comment(0)
N
5

FYI, I just did some test using the method above and if we use multi ping (10 requests)

ping -c10 8.8.8.8 &> /dev/null ; echo $?

the result of multi ping command will be "0" if at least one of ping result reachable, and "1" in case where all ping requests are unreachable.

Noblewoman answered 3/11, 2014 at 11:21 Comment(1)
Send the output to a file in /tmp, check it from a GUI like lxpanel and you've got an up/down indicator for your tray. Or lately I'm into loops like ping every 30 seconds then sound a bell character and quit when a ping succeeds. Maybe with Termux on an Android phone. It ceases to be a one-liner though.Rasia
F
1
up=`fping -r 1 $1 `
if [ -z "${up}" ]; then
    printf "Host $1 not responding to ping   \n"
    else
    printf "Host $1 responding to ping  \n"
fi
Foretopgallant answered 30/9, 2016 at 23:8 Comment(0)
A
0
for i in `cat Hostlist`
do  
  ping -c1 -w2 $i | grep "PING" | awk '{print $2,$3}'
done
Aloe answered 10/9, 2020 at 18:46 Comment(0)
R
-1

This seems to work moderately well in a terminal emulator window. It loops until there's a connection then stops.

#!/bin/bash

# ping in a loop until the net is up

declare -i s=0
declare -i m=0
while ! ping -c1 -w2 8.8.8.8 &> /dev/null ;
do
  echo "down" $m:$s
  sleep 10
  s=s+10
  if test $s -ge 60; then
    s=0
    m=m+1;
  fi
done
echo -e "--------->>  UP! (connect a speaker) <<--------" \\a

The \a at the end is trying to get a bel char on connect. I've been trying to do this in LXDE/lxpanel but everything halts until I have a network connection again. Having a time started out as a progress indicator because if you look at a window with just "down" on every line you can't even tell it's moving.

Rasia answered 7/1, 2020 at 21:35 Comment(1)
-w2 should be -W2 (with capital W)Ivetteivetts
C
-1

I liked the idea of checking a list like:

for i in `cat Hostlist`
do  
  ping -c1 -w2 $i | grep "PING" | awk '{print $2,$3}'
done

but that snippet doesn't care if a host is unreachable, so is not a great answer IMHO.

I ran with it and wrote

for i in `cat Hostlist`
do
  ping -c1 -w2 $i >/dev/null 2>&1 ; echo $i $?
done

And I can then handle each accordingly.

Capping answered 10/12, 2020 at 19:0 Comment(2)
Welcome to Stack Overflow! For future reference, you should use the backtick character (`) instead of the period character (.) to define code fences. This will fix your code block formatting.Bathos
I don't believe I used a period in my snippet?Capping
B
-1

check host every one second and send message when host is reach

while :;do ping -c 1 -w 1 -q 8.8.8.8 &>/dev/null && /root/telegram-send.sh "Host reacheble now" && break || sleep 1;done
Bobbysoxer answered 23/12, 2021 at 12:39 Comment(1)
That's not what OP asked for though.Cowcatcher

© 2022 - 2024 — McMap. All rights reserved.