3

Apparently MacOs's ping has a -o option which means "return once the first successful response is received" or something like that. See: https://stackoverflow.com/a/6119327/4561887.

Is there an equivalent on Linux Ubuntu? How can we best achieve the same result?

My version is ping utility, iputils-s20161105, as returned by ping -V.

Update: I found the MacOs documentation here: https://ss64.com/osx/ping.html

-o         Exit successfully after receiving one reply packet.
  • This looks potentially promising: https://serverfault.com/a/42382/357116 – Gabriel Staples Nov 18 '21 at 06:05
  • As does this: https://serverfault.com/a/545408/357116 – Gabriel Staples Nov 18 '21 at 06:07
  • 1
    @A.B, no, after testing fping, I can say the following: fping 192.168.0.1 behaves like ping -c 4 -W 1 192.168.0.1 when the network is down, and like ping -c 1 192.168.0.1 when the network is up. fping returns immediately if the network is up, and tries 4 times (1 initial try plus 3 retries, with a 1 second timeout after each attempt) if the network is down, returning after 4 timeouts = 4 sec total. ping -o would return immediately if the network is up, but loop forever until the network comes up, then return, if the network was down. – Gabriel Staples Nov 18 '21 at 18:44
  • Did you read the man page? – symcbean Nov 18 '21 at 22:46
  • 1
    @symcbean, which man page? The man page for Linux's ping, MacOS's ping, or fping? – Gabriel Staples Nov 18 '21 at 22:51

3 Answers3

5
Using ping:
ping -c 1 ping_address
ping -c 1 192.168.1.68 #example
  • c argument is for counting packets, so you need only 1 that's why --> -c 1

  • After running a command, you can check its exit status by using the $? command. After using ping, you should see a 0 (success, OK). Otherwise, you get either 1 or 2 after typing $?, it means an error.

PING output returns with echo:
1 received, 0%   packet loss -> (Success echo $? #returns 0
0 received, 100% packet loss -> (Failure)echo $? #returns 1

An infinite loop that stops when the service becomes available, pipe it into a while loop.

while ! ping -c1 -W1 192.168.1.68; :;done
  • While NOT ! alive, keep looping and always exit with an exit status that succeeds :
  • -W1 is for waiting 1 second

copy this into a file with name pingo

#!/usr/bin/bash
while ! ping -c1 -W1 "$1"; do
    : && echo "NOT ALIVE"
done
  • : is a Null command or No effect, the command does nothing or exit status always succeeds, in this case, similar to typing true. Is a Bourne Shell Builtin
  • The variable $1 represents the first command-line argument, which in this case is a numeric IP address. For instance, pingo 192.168.1.68 would substitute $1 with the specified IP address."
bin directory for user

Create a folder with the name bin. Inside that folder, create a file with the name pingo and allow the file to be executed as a program.

mkdir ~/bin    # make directory at /home/user/bin
cd ~/bin       # change to /home/user/bin directory
touch pingo    # create file pingo copy the above command and save it
chmod +x pingo # give permission to execute
  • the /home/user/bin folder is the default location for your own programs, which can be executed in the terminal from anywhere, just like the 'ping' command.

That's it, now you can run pingo with any address, just like ping -o.

From PING manual:
  • If ping does not receive any reply packets at all, it will exit with code 1.
  • If a packet count and deadline are both specified, and fewer than count packets are received by the time the deadline has arrived, it will also exit with code 1. On other error, it exits with code 2. Example:
    • count 1 and wait for 2 seconds BUT nothing has arrived at second 2, THEN exit with error
  • Otherwise, it exits with code 0. [ OK ]

This makes it possible to use the exit code to see if a host is alive or not.

AlexPixel
  • 290
  • Yes that was simple after all. – A.B Nov 18 '21 at 18:31
  • 1
    ping -o would return immediately if the network is up, but loop forever until the network comes up, then return, if the network was down. ping -c 1 returns after a single ping regardless of whether or not the network is up, so it doesn't have similar behavior to ping -o. – Gabriel Staples Nov 18 '21 at 18:47
  • @GabrielStaples infinite loop can be achieved in ping with ping -W0 ping_address, but to check if network is up or down, it's not going to work. It's going to be infinite out --> not network or when network is UP will output --> network working, going to update answer, so you have ping -o – AlexPixel Nov 18 '21 at 20:33
  • 1
    Would while ! ping -c1 192.168.1.68; do sleep 1; done be a little simpler? And a bit easier on the network. – doneal24 Nov 18 '21 at 23:33
  • 1
    Replace sleep 1 with : if you don't want a delay in ping attempts. – doneal24 Nov 18 '21 at 23:39
4

There can be various workarounds to try and make ping behave like Mac's ping -o but there's the command fping that does just that, and is intended to be used in scripts and for multiple targets at once. From manual:

[...]

In the default mode, if a target replies, it is noted and removed from the list of targets to check; if a target does not respond within a certain time limit and/or retry limit it is designated as unreachable.

[...]

Examples:

$ time fping example.com
example.com is alive

real 0m0.074s user 0m0.000s sys 0m0.004s $ echo $? 0

$ fping 192.0.2.3 127.0.0.1 ICMP Host Unreachable from 203.0.113.1 for ICMP Echo sent to 192.0.2.3 127.0.0.1 is alive ICMP Host Unreachable from 203.0.113.1 for ICMP Echo sent to 192.0.2.3 ICMP Host Unreachable from 203.0.113.1 for ICMP Echo sent to 192.0.2.3 192.0.2.3 is unreachable $ echo $? 1 $ fping -q 192.0.2.3 $ echo $? 1

A.B
  • 36,364
  • 2
  • 73
  • 118
  • It looks like fping is installable on Ubuntu with sudo apt update && sudo apt install fping. – Gabriel Staples Nov 18 '21 at 16:59
  • Indeed the manual I linked is from Ubuntu. – A.B Nov 18 '21 at 17:24
  • 2
    fping 192.168.0.1 behaves like ping -c 4 -W 1 192.168.0.1 when the network is down, and like ping -c 1 192.168.0.1 when the network is up. fping returns immediately if the network is up, and tries 4 times (1 initial try plus 3 retries, with a 1 second timeout after each attempt) if the network is down, returning after 4 timeouts = 4 sec total. ping -o would return immediately if the network is up, but loop forever until the network comes up, then return, if the network was down. – Gabriel Staples Nov 18 '21 at 18:44
0

After my own study plus reviewing the two other answers here, I have concluded: there is no equivalent on Linux to the ping -o command which exists on MacOS. Neither ping -c 1 nor fping have the same behavior. The best you can do is emulate its behavior by using fping or ping in a loop which returns once the return code of either of those functions is 0, indicating a ping success.

As I put in my comment here:

after testing fping, I can say the following: fping 192.168.0.1 behaves like ping -c 4 -W 1 192.168.0.1 when the network is down, and like ping -c 1 192.168.0.1 when the network is up. fping returns immediately if the network is up, and tries 4 times (1 initial try plus 3 retries, with a 1 second timeout after each attempt) if the network is down, returning after 4 timeouts = 4 sec total. ping -o would return immediately if the network is up, but loop forever until the network comes up, then return, if the network was down.

So, leveraging the good work by @AlexPixel, here is my answer. This form catches the Ctrl + C kill signal to allow you to forcefully exit the infinite while loop, it ensures you enter a parameter for the IP address when you run it, and it prints an attempt count and a message when the IP address comes alive.

ping_loop.sh:

#!/bin/bash

IP_ADDR="$1" if [ -z $IP_ADDR ]; then echo "You must enter the IP Address as the first parameter!" exit 1 fi

to enable Ctrl + C inside the infinite while loop

trap 'printf "%s\n" "Ctr + C"; exit 2' SIGINT count=0 while true; do echo "Attempt: $count" ((count++)) ping -c 1 -W 5 "$IP_ADDR" return_code=$? if [ $return_code -eq 0 ]; then echo "== DEVICE AT IP '$IP_ADDR' IS ALIVE! ==" break; fi done

Make executable with chmod +x ping_loop.sh. "Install" it like this:

  1. Create a symlink in ~/bin to this script so you can run it from anywhere:
    cd /path/to/here
    mkdir -p ~/bin
    ln -si "${PWD}/ping_loop.sh" ~/bin/gs_ping_loop
    ln -si "${PWD}/ping_loop.sh" ~/bin/ping_loop
    
  2. Now you can use the ping_loop or gs_ping_loop command directly anywhere you like. This assumes ~/bin is in your PATH. If using Ubuntu and this is the first time creating ~/bin, you may need to log out and log back in for your ~/.profile file to make this take effect because it will automatically add that dir to your path.

Test it with:

ping_loop 10.0.0.1

Now you can read this other answers here to see how to forcefully bring that IP address up and down for ping testing: How to create Ethernet interface at a specific IP address that I can ping and force to sometimes reply and sometimes not?.

One approach is this:

# block traffic to 10.0.0.1
sudo iptables -I OUTPUT 1 -p icmp --icmp-type echo-request -d 10.0.0.1 -j DROP

allow traffic to 10.0.0.1

NB: you must run this as many times as you ran the cmd above in order to

allow traffic. So, if you ran the cmd above an unknown number of times,

run this command as many times as it takes until you see:

iptables: Bad rule (does a matching rule exist in that chain?).

sudo iptables -D OUTPUT -p icmp --icmp-type echo-request -d 10.0.0.1 -j DROP

Sample output. I started with the traffic blocked via the command above, and then unblocked it with the other command above to make ping pass and allow the script to end. You can see the == DEVICE AT IP '10.0.0.1' IS ALIVE! == print at the end.

$ gs_ping_loop 10.0.0.1
Attempt: 0
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
ping: sendmsg: Operation not permitted

--- 10.0.0.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms

Attempt: 1 PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. ping: sendmsg: Operation not permitted

--- 10.0.0.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms

Attempt: 2 PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. ping: sendmsg: Operation not permitted

--- 10.0.0.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms

Attempt: 3 PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. ping: sendmsg: Operation not permitted

--- 10.0.0.1 ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms

Attempt: 4 PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data. 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=4.20 ms

--- 10.0.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 4.204/4.204/4.204/0.000 ms == DEVICE AT IP '10.0.0.1' IS ALIVE! ==


Here is my final form of ping_loop.sh in my eRCaGuy_dotfiles repo: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/master/useful_scripts/ping_loop.sh <-- this is where I'll keep the latest and most-up-to-date version of this code!

See also:

  1. The 3 answers here, my own included, on how to turn an IP address's "pingability" on and off. This allows you to test the ping_loop script above: How to create Ethernet interface at a specific IP address that I can ping and force to sometimes reply and sometimes not?