[bash] How to test an Internet connection with bash?

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?

This question is related to bash connection ping

The answer is


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

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


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


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.)


Execute the following command to check whether a web site is up, and what status message the web server is showing:

curl -Is http://www.google.com | head -1 HTTP/1.1 200 OK

Status code ‘200 OK’ means that the request has succeeded and a website is reachable.


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

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

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).


Without ping

#!/bin/bash

wget -q --spider http://google.com

if [ $? -eq 0 ]; then
    echo "Online"
else
    echo "Offline"
fi

-q : Silence mode

--spider : don't get, just check page availability

$? : shell return code

0 : shell "All OK" code

Without wget

#!/bin/bash

echo -e "GET http://google.com HTTP/1.0\n\n" | nc google.com 80 > /dev/null 2>&1

if [ $? -eq 0 ]; then
    echo "Online"
else
    echo "Offline"
fi

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

This works on both MacOSX and Linux:

#!/bin/bash

ping -q -w1 -c1 google.com &>/dev/null && echo online || echo offline

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

Super Thanks to user somedrew for their post here: https://bbs.archlinux.org/viewtopic.php?id=55485 on 2008-09-20 02:09:48

Looking in /sys/class/net should be one way

Here's my script to test for a network connection other than the loop back. I use the below in another script that I have for periodically testing if my website is accessible. If it's NOT accessible a popup window alerts me to a problem.

The script below prevents me from receiving popup messages every five minutes whenever my laptop is not connected to the network.

#!/usr/bin/bash

# Test for network conection
for interface in $(ls /sys/class/net/ | grep -v lo);
do
  if [[ $(cat /sys/class/net/$interface/carrier) = 1 ]]; then OnLine=1; fi
done
if ! [ $OnLine ]; then echo "Not Online" > /dev/stderr; exit; fi

Note for those new to bash: The final 'if' statement tests if NOT [!] online and exits if this is the case. See man bash and search for "Expressions may be combined" for more details.

P.S. I feel ping is not the best thing to use here because it aims to test a connection to a particular host NOT test if there is a connection to a network of any sort.

P.P.S. The Above works on Ubuntu 12.04 The /sys may not exist on some other distros. See below:

Modern Linux distributions include a /sys directory as a virtual filesystem (sysfs, comparable to /proc, which is a procfs), which stores and allows modification of the devices connected to the system, whereas many traditional UNIX and Unix-like operating systems use /sys as a symbolic link to the kernel source tree.[citation needed]

From Wikipedia https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard


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.


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.


Checking Google's index page is another way to do it:

#!/bin/bash

WGET="/usr/bin/wget"

$WGET -q --tries=20 --timeout=10 http://www.google.com -O /tmp/google.idx &> /dev/null
if [ ! -s /tmp/google.idx ]
then
    echo "Not Connected..!"
else
    echo "Connected..!"
fi

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


Similarly to @Jesse's answer, this option might be much faster than any solution using ping and perhaps slightly more efficient than @Jesse's answer.

find /sys/class/net/ -maxdepth 1 -mindepth 1 ! -name "*lo*" -exec sh -c 'cat "$0"/carrier 2>&1' {} \; | grep -q '1'

Explenation:

This command uses find with -exec to run command on all files not named *lo* in /sys/class/net/. These should be links to directories containing information about the available network interfaces on your machine.

The command being ran is an sh command that checks the contents of the file carrier in those directories. The value of $interface/carrier has 3 meanings - Quoting:

It seems there are three states:

  • ./carrier not readable (for instance when the interface is disabled in Network Manager).
  • ./carrier contain "1" (when the interface is activated and it is connected to a WiFi network)
  • ./carrier contain "0" (when the interface is activated and it is not connected to a WiFi network)

The first option is not taken care of in @Jesse's answer. The sh command striped out is:

# Note: $0 == $interface
cat "$0"/carrier 2>&1

cat is being used to check the contents of carrier and redirect all output to standard output even when it fails because the file is not readable. If grep -q finds "1" among those files it means there is at least 1 interface connected. The exit code of grep -q will be the final exit code.

Usage

For example, using this command's exit status, you can use it start a gnubiff in your ~/.xprofile only if you have an internet connection.

online() {
    find /sys/class/net/ -maxdepth 1 -mindepth 1 ! -name "*lo*" -exec sh -c 'cat "$0"/carrier 2>&1 > /dev/null | grep -q "1" && exit 0' {} \;
}
online && gnubiff --systemtray --noconfigure &

Reference


Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to connection

Apache Server (xampp) doesn't run on Windows 10 (Port 80) "Proxy server connection failed" in google chrome Failed to connect to mysql at 127.0.0.1:3306 with user root access denied for user 'root'@'localhost'(using password:YES) "The underlying connection was closed: An unexpected error occurred on a send." With SSL Certificate Login to Microsoft SQL Server Error: 18456 How do I start Mongo DB from Windows? java.rmi.ConnectException: Connection refused to host: 127.0.1.1; mySQL Error 1040: Too Many Connection org.apache.http.conn.HttpHostConnectException: Connection to http://localhost refused in android What is the functionality of setSoTimeout and how it works?

Examples related to ping

Docker - Ubuntu - bash: ping: command not found How to ping a server only once from within a batch file? Ping with timestamp on Windows CLI ping response "Request timed out." vs "Destination Host unreachable" Can't ping a local VM from the host Checking host availability by using ping in bash scripts Fastest way to ping a network range and return responsive hosts? Why can I ping a server but not connect via SSH? How to ping multiple servers and return IP address and Hostnames using batch script? Multiple ping script in Python