How to test an Internet connection with bash?

后端 未结 19 1211
青春惊慌失措
青春惊慌失措 2020-11-27 09:26

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

相关标签:
19条回答
  • 2020-11-27 10:21

    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
    0 讨论(0)
  • 2020-11-27 10:22

    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

    • Help testing special file in /sys/class/net/
    • find -exec a shell function?
    0 讨论(0)
  • 2020-11-27 10:24

    This is notably faster than any solution posted here, and can be used to test a specific host too.

    The trick is solving the IP using a less busy DNS solver than the one employed by ping:

    validateConnection () {
        host="${1}"
        ip=$(getent ahostsv4 "${host}" | awk '{ print $1 }' | head -n1)
        ping -c1 "${ip}" 2>&1 >"/dev/null"
    
        if [ "${?}" -ne 0 ]; then
            echo "No connection to: ${host}" >&2
            exit 1
        fi
    }
    
    0 讨论(0)
  • 2020-11-27 10:26

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

    0 讨论(0)
  • 2020-11-27 10:27

    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
    
    0 讨论(0)
  • 2020-11-27 10:28

    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.

    0 讨论(0)
提交回复
热议问题