66

How can I ping a certain address and when found, stop pinging.

I want to use it in a bash script, so when the host is starting up, the script keeps on pinging and from the moment the host is available, the script continues...

1

18 Answers 18

105

A further simplification of Martynas' answer:

until ping -c1 www.google.com >/dev/null 2>&1; do :; done

note that ping itself is used as the loop test; as soon as it succeeds, the loop ends. The loop body is empty, with the null command ":" used to prevent a syntax error.

Update: I thought of a way to make Control-C exit the ping loop cleanly. This will run the loop in the background, trap the interrupt (Control-C) signal, and kill the background loop if it occurs:

ping_cancelled=false    # Keep track of whether the loop was cancelled, or succeeded
until ping -c1 "$1" >/dev/null 2>&1; do :; done &    # The "&" backgrounds it
trap "kill $!; ping_cancelled=true" SIGINT
wait $!          # Wait for the loop to exit, one way or another
trap - SIGINT    # Remove the trap, now we're done with it
echo "Done pinging, cancelled=$ping_cancelled"

It's a bit circuitous, but if you want the loop to be cancellable it should do the trick.

9
  • 8
    To add 'sleep' (e.g. each 5 secs): while ! ping -c1 www.google.com &>/dev/null; do sleep 5; done
    – lepe
    Apr 11, 2015 at 3:55
  • 4
    By default ping will wait 10 seconds before giving up on its ping. If you want to reduce that to 1 second, you can use -w1. Dec 1, 2016 at 19:44
  • 2
    @JackO'Connor Using BSD ping, this is a capital -W1
    – Luke Exton
    Mar 31, 2017 at 19:17
  • My only issue with this answer is it doesn't handle SIGINT well. You are unable to stop this from the command line, if it is never going to connect for whatever reason.
    – Luke Exton
    Mar 31, 2017 at 19:18
  • 1
    @LukeExton That's due to the ways bash and ping handle SIGINT, and how they interact (see this question). I don't have a very good solution, but there's a kluge: use Control-Z to stop the script, then kill %1 to kill it. Mar 31, 2017 at 21:47
42

I know the question is old... and specifically asks regarding ping, but I wanted to share my solution.

I use this when rebooting hosts to know when I can SSH back into them again. (Since ping will respond for several seconds before sshd is started.)

until nc -vzw 2 $host 22; do sleep 2; done
4
  • 2
    That's exactly what I searched for, thanks! You can even interrupt the call with CTRL+C which is not the case for some other solutions in this thread.
    – Alex
    Jan 19, 2014 at 8:58
  • 1
    Note this works for BSD versions of netcat. The nmap.org version in Linux distros is implemented differently, so the -z option doesn't exist, and the -w option waits for a connection but doesn't close when successful. Works great in OSX, though.
    – Paul
    Jan 8, 2016 at 16:59
  • This is why you don't stop at the first (fine) answer. This is the answer to the real problem.
    – Leo
    Nov 3, 2019 at 9:36
  • 1
    Regarding whether this works on "Linux distros" or not, the default version of netcat in Ubuntu 18.04 is of the BSD variety and works exactly as described here.
    – spikyjt
    Feb 25, 2020 at 13:47
30

You can do a loop, send one ping and depending on the status break the loop, for example (bash):

while true; do ping -c1 www.google.com > /dev/null && break; done

Putting this somewhere in your script will block, until www.google.com is pingable.

4
  • 2
    This while true and break is the cleaner solution, IMO.
    – Dan Carley
    Jul 17, 2009 at 8:26
  • 1
    @DanCarley I'm going to disagree with you for the reason that in the accepted answer, the null command version, one could substitute a sleep/wait to give your CPU a break, and in which case I would call it the cleaner solution. The other problem with this answer, is that it requires the reader/user to understand how the && works, and how that if the first command fails, it won't be called. This is just an opinion, like your own.
    – Hamid
    Jan 11, 2015 at 19:06
  • 1
    The main difference I see between this solution and the accepted one, is that this one will echo the error message "unknown host ..." until its found. To add sleep (e.g. each 5 secs): while true; do sleep 5; ping ...
    – lepe
    Apr 10, 2015 at 2:23
  • this cannot be cancelled with ctrl+c
    – cweiske
    Mar 18 at 10:32
11

Ping the target host once. Check if the ping succeeded (return value of ping is zero). If host is not alive, ping again.

The following code can be saved as a file and called with the hostname as argument, or stripped of the first and last line and used as function within an existing script (waitForHost hostname).

The code does not evaluate the cause for failure if the ping does not result in a response, thus looping forever if the host does not exist. My BSD manpage lists the meaning of each return value, while the linux one does not, so I guess this might not be portable, that's why I left it out.

#!/bin/bash

PING=`which ping`

function waitForHost
{
    if [ -n "$1" ]; 
    then
        waitForHost1 $1;
    else
        echo "waitForHost: Hostname argument expected"
    fi
}

function waitForHost1
{
    reachable=0;
    while [ $reachable -eq 0 ];
    do
    $PING -q -c 1 $1
    if [ "$?" -eq 0 ];
    then
        reachable=1
    fi
    done
    sleep 5
}
waitForHost $1
0
9
UNREACHEABLE=1;
while [ $UNREACHEABLE -ne "0" ]; 
   do ping -q -c 1 HOST &> /dev/null; UNREACHEABLE=$?; sleep 1;
done

You may remove sleep 1, it's only here to prevent any flooding problem in case where the host would be reacheable but ping would not exit with code 0.

4

Please see good options at stackoverflow. Here is a sample in bash, you will have to loop over the following code until it returns a successfull ping result.


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

0
3

any of the above loops can also be used with fping rather than ping which, IMO, is better suited for use in scripts than ping itself. See fping(1) for details.

while ! fping -q $HOSTNAMES ; do :; done

also useful for testing if machines are up before doing something on them. A simple example:

for h in HOST1 HOST2 HOST3 ; do
  if fping -q $h ; then
     echo -n "$h : "
     ssh $h "uname -a"
  fi
done
3
  • How do you kill the while loop!? Ctrl + C won't kill it, neither will Ctrl + D. Nov 18, 2021 at 18:11
  • @GabrielStaples 1. the loop will terminate when fping returns with a 0 (success) exit code - from man fping: "Exit status is 0 if all the hosts are reachable, 1 if some hosts were unreachable, 2 if any IP addresses were not found, 3 for invalid command line arguments, and 4 for a system call failure.". 2. it's a minimum working example, not a complete solution. the purpose is to show the method. 3. do something other than the : empty statement in the loop.
    – cas
    Nov 19, 2021 at 1:20
  • cas, thanks. I added my own example here: Equivalent of ping -o on Linux. To enable Ctrl + C to work inside the while loop I discovered you can add this before the while loop: trap 'printf "%s\n" "Ctr + C"; exit 2' SIGINT. You can see that in my example too. Nov 19, 2021 at 6:28
3

For macOS users, ping has the option -o specifically for this:

-o      Exit successfully after receiving one reply packet.

So the command is simple:

ping -o www.google.com

It returns 0 when the host has been successfully pinged once.

1

This will try a given number of times.

t=4; c=0; r=0; until ping -c 1 hostname.com >/dev/null 2>&1 || ((++c >= t)); do r=$?; done; echo $r

Instead of echoing $r, you can test it and act according to its value:

if ((r)); then echo 'Failed to contact host'; else echo 'Continuing with script'; fi
1

In general I want to wait for my database or other server to come up, but I don't want to wait too long. The following code waits for 10 seconds, then sets the exit code if the server did not appear within the time limit.

If the server appears before the time limit, the loop will be short-circuited so the next bit of code can run.

for i in `seq 1 10`; do date ; sleep 1 ; ping -c1 ${HOST} &>/dev/null && break ; done
1

To nicely handle the SIGINT on BSD ping.

HOST=google.com NO=1; while [ $NO -ne 0 ]; do ping -W1 -c1 $HOST &>/dev/null; NO=$?;echo "$(date) ($HOST) $NO" ; done; echo "$(date) ($HOST) reachable"

as a function

ping_until(){
  local NO=1
  while [ $NO -ne 0 ]; do
    ping -W1 -c1 $1 &>/dev/null; NO=$?
    # Optionally block ICMP flooding
    # sleep 1
    echo "$(date) ($1) ($NO)"
  done
}
1

Folks, yes this is old. A lot of good answers here.

But I just found what I call a beauty! In case someone likes native ping counter:

ping $HOST | sed "/ ms$/ q"

Isn't this fascinating? :)

[p@localhost ~]$ VBoxManage startvm VGTU-2021-LDVM1 --type headless && \
> ping 192.168.10.14 | sed "/ ms$/ q"
Waiting for VM "VGTU-2021-LDVM1" to power on...
VM "VGTU-2021-LDVM1" has been successfully started.
PING 192.168.10.14 (192.168.10.14) 56(84) bytes of data.
From 192.168.10.8 icmp_seq=1 Destination Host Unreachable
From 192.168.10.8 icmp_seq=2 Destination Host Unreachable
  ...
From 192.168.10.8 icmp_seq=35 Destination Host Unreachable
From 192.168.10.8 icmp_seq=36 Destination Host Unreachable
64 bytes from 192.168.10.14: icmp_seq=37 ttl=64 time=2003 ms
0

I've been using the following function. I like it because I can tell it to stop trying after a while:

#!/usr/bin/env bash

function networkup {
  # Initialize number of attempts
  reachable=$1
  while [ $reachable -ne 0 ]; do
    # Ping supplied host
    ping -q -c 1 -W 1 "$2" > /dev/null 2>&1
    # Check return code
    if [ $? -eq 0 ]; then
      # Success, we can exit with the right return code
      echo 0
      return
    fi
    # Network down, decrement counter and try again
    let reachable-=1
    # Sleep for one second
    sleep 1
  done
  # Network down, number of attempts exhausted, quiting
  echo 1
}

It can be used like this to launch something:

# Start-up a web browser, if network is up
if [ $(networkup 60 www.google.com) -eq 0 ]; then
  firefox &
fi
0

A further enhancement to Gordon Davisson's answer:

until $(ping -c1 www.google.com &>/dev/null); do :; done

with the surrounding '$()' a subshell is started and therefore you can use Control-C to terminate the loop in the event that the host does not become available.

0
$ cat bin/check_host_up.sh
HOST=$1
PINGCMD="ping -c1 $HOST >/dev/null 2>&1"
while true; do
    eval $PINGCMD && sleep 1 && echo -n . ||    \
    (                                           \
        echo && echo "(${HOST}) down:" $(date) &&         \
            until eval $PINGCMD; do :; done &&  \
        echo "(${HOST}) up  :" $(date)
    );
done
0

ping $HOSTNAME | grep from -m 1

0

grep works on lines, so:

ping google.com | grep "not"

0

Here's a solution that doesn't require cobbling together a shell script:

watch -n 1 -g -- ping -W 1 -c 1 192.0.2.1

Every second, watch will run ping, which will sent one ICMP echo request to 192.0.2.1, giving up if it does not recieve a response within one second.

watch will exit when the output of that command changes, i.e., when the host becomes available.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .