How to test an Internet connection with bash?

Viewed 283941

How can an internet connection be tested without pinging some website? I mean, what if there is a connection but the site is down? Is there a check for a connection with the world?

20 Answers

Ping your default gateway:

#!/bin/bash
ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null && echo ok || echo error

In Bash, using it's network wrapper through /dev/{udp,tcp}/host/port:

if : >/dev/tcp/8.8.8.8/53; then
  echo 'Internet available.'
else
  echo 'Offline.'
fi

(: is the Bash no-op, because you just want to test the connection, but not processing.)

The top answer misses the fact that you can have a perfectly stable connection to your default gateway but that does not automatically mean you can actually reach something on the internet. The OP asks how he/she can test a connection with the world. So I suggest to alter the top answer by changing the gateway IP to a known IP (x.y.z.w) that is outside your LAN.

So the answer would become:

ping -q -w 1 -c 1 x.y.z.w > /dev/null && echo ok || echo error

Also removing the unfavored backticks for command substitution[1].

If you just want to make sure you are connected to the world before executing some code you can also use:

if ping -q -w 1 -c 1 x.y.z.w > /dev/null; then
    # more code
fi

I've written scripts before that simply use telnet to connect to port 80, then transmit the text:

HTTP/1.0 GET /index.html

followed by two CR/LF sequences.

Provided you get back some form of HTTP response, you can generally assume the site is functioning.

make sure your network allow TCP traffic in and out, then you could get back your public facing IP with the following command

curl ifconfig.co

The top voted answer does not work for MacOS so for those on a mac, I've successfully tested this:

GATEWAY=`route -n get default | grep gateway`
if [ -z "$GATEWAY" ]
  then
    echo error
else
  ping -q -t 1 -c 1 `echo $GATEWAY | cut -d ':' -f 2` > /dev/null && echo ok || echo error
fi

tested on MacOS High Sierra 10.12.6

If your local nameserver is down,

ping 4.2.2.1

is an easy-to-remember always-up IP (it's actually a nameserver, even).

This bash script continuously check for Internet and make a beep sound when the Internet is available.

#!/bin/bash
play -n synth 0.3 sine 800 vol 0.75
while :
do
pingtime=$(ping -w 1 8.8.8.8 | grep ttl)
if [ "$pingtime" = "" ] 
then 
   pingtimetwo=$(ping -w 1 www.google.com | grep ttl) 
   if [ "$pingtimetwo" = "" ] 
   then 
       clear ; echo 'Offline'
   else
       clear ; echo 'Online' ; play -n synth 0.3 sine 800 vol 0.75
   fi 
else
    clear ; echo 'Online' ; play -n synth 0.3 sine 800 vol 0.75
fi
sleep 1
done

shortest way: fping 4.2.2.1 => "4.2.2.1 is alive"

i prefer this as it's faster and less verbose output than ping, downside is you will have to install it.

you can use any public dns rather than a specific website.

fping -q google.com && echo "do something because you're connected!"

-q returns an exit code, so i'm just showing an example of running something you're online.

to install on mac: brew install fping; on ubuntu: sudo apt-get install fping

Ping was designed to do exactly what you're looking to do. However, if the site blocks ICMP echo, then you can always do the telnet to port 80 of some site, wget, or curl.

Pong doesn't mean web service on the server is running; it merely means that server is replying to ICMP echo. I would recommend using curl and check its return value.

If your goal is to actually check for Internet access, many of the existing answers to this question are flawed. A few things you should be aware of:

  1. It's possible for your computer to be connected to a network without that network having internet access
  2. It's possible for a server to be down without the entire internet being inaccessible
  3. It's possible for a captive portal to return an HTTP response for an arbitrary URL even if you don't have internet access

With that in mind, I believe the best strategy is to contact several sites over an HTTPS connection and return true if any of those sites responds.

For example:

connected_to_internet() {
  test_urls="\
  https://www.google.com/ \
  https://www.microsoft.com/ \
  https://www.cloudflare.com/ \
  "

  processes="0"
  pids=""

  for test_url in $test_urls; do
    curl --silent --head "$test_url" > /dev/null &
    pids="$pids $!"
    processes=$(($processes + 1))
  done

  while [ $processes -gt 0 ]; do
    for pid in $pids; do
      if ! ps | grep "^[[:blank:]]*$pid[[:blank:]]" > /dev/null; then
        # Process no longer running
        processes=$(($processes - 1))
        pids=$(echo "$pids" | sed --regexp-extended "s/(^| )$pid($| )/ /g")

        if wait $pid; then
          # Success! We have a connection to at least one public site, so the
          # internet is up. Ignore other exit statuses.
          kill -TERM $pids > /dev/null 2>&1 || true
          wait $pids
          return 0
        fi
      fi
    done
    # wait -n $pids # Better than sleep, but not supported on all systems
    sleep 0.1
  done

  return 1
}

Usage:

if connected_to_internet; then
  echo "Connected to internet"
else
  echo "No internet connection"
fi

Some notes about this approach:

  1. It is robust against all the false positives and negatives I outlined above
  2. The requests all happen in parallel to maximize speed
  3. It will return false if you technically have internet access but DNS is non-functional or your network settings are otherwise messed up, which I think is a reasonable thing to do in most cases

For the fastest result, ping a DNS server:

ping -c1 "8.8.8.8" &>"/dev/null"

if [[ "${?}" -ne 0 ]]; then
    echo "offline"
elif [[ "${#args[@]}" -eq 0 ]]; then
    echo "online"
fi

Available as a standalone command: linkStatus

If you want to handle captive portals, you can do this oneliner:

if [[ $(curl -s -D - http://www.gstatic.com/generate_204 2>/dev/null | head -1 | cut -d' ' -f 2) == "204" ]]; then
  echo 'online'
else
  echo 'offline'
fi

Or if you want something more readable that can differentiate captive portals from lack of signal:

function is_online() {
  # Test signal
  local response
  response=$(curl --silent --dump-header - http://www.gstatic.com/generate_204 2> /dev/null)
  if (($? != 0)); then return 2; fi
  # Test captive portal
  local status=$(echo $response | head -1 | cut -d' ' -f 2)
  ((status == "204"))
}
is_online && echo online || echo offline
Related