How to test if link is up in bash script - bash

using this script I am trying to detect if there is a network link. I am failing in putting multiple commands in one line (ethtool...). What to do?
#!/bin/bash
COMMAND="( /sbin/ethtool eth0 ) | ( /bin/grep \"Link detected: yes\" ) | ( wc -l )"
ONLINE=eval $COMMAND
if $ONLINE; then
echo "Online"
else
echo "Not online"
fi

You simply need
if /sbin/ethtool eth0 | grep -q "Link detected: yes"; then
echo "Online"
else
echo "Not online"
fi
Also if you want to encapsulate checking, simply use a function:
function check_eth {
set -o pipefail # optional.
/sbin/ethtool "$1" | grep -q "Link detected: yes"
}
if check_eth eth0; then
echo "Online"
else
echo "Not online"
fi
How it works: if simply interprets a command in front of it and check if the return value of $? is 0. grep returns 0 when it finds a match on its search. And so because of this you don't need to use wc and compare its output to 1.

It appears your script question has been answered. Under Linux I would implement this by reading sysfs directly.
function ifup {
if [[ ! -d /sys/class/net/${1} ]]; then
printf 'No such interface: %s\n' "$1" >&2
return 1
else
[[ $(</sys/class/net/${1}/operstate) == up ]]
fi
}
if ifup enp7s0; then
echo Online
else
echo 'Not online'
fi
My second choice would probably be ip link.
# Returns true if iface exists and is up, otherwise false.
function ifup {
typeset output
output=$(ip link show "$1" up) && [[ -n $output ]]
}
...

At least at my machine (Debian 7.5) only root is allowed to determine link status with ethtool and it is not installed in all distributions.
I can think of three alternatives to ethtool to determine network link status:
ifconfig eth0
ip link show (you have to grep for your interface and "state UP" or "state DOWN")
send one ping to some known online host (could also be given as parameter) in your network (ping -c 1 -w 1 ip_address &> /dev/null, -c specifies the number of packets to send and -w is the timeout in seconds)

Looking at your question can you use /sbin/ifconfig eth0 and check if it has inet or inet6 address, or you can also check for the line UP BROADCAST RUNNING MULTICAST. If the network is down the line will be UP BROADCAST MULTICAST
You can check the man page of ifconfig for more information.

Different functions for different purposes:
## (1) check if there is any interface UP
## loopback interface (lo) is excluded
_isInterfaceUp(){
ip link|grep -v '[0-9]: lo:' 2>/dev/null|grep -l '^[0-9].* UP ' >/dev/null 2>&1
}
## (2) check if there is a valid default gateway in the routing table
## note: routes via non-active interfaces are excluded
_isNetworkUp(){
ip route|grep '^default via ' 2>/dev/null|grep -lv 'linkdown' >/dev/null 2>&1
}
## (3) check if Internet is connected properly
_isInternetConnected(){
if ! ping -c 4 8.8.8.8 >/dev/null 2>&1; then
## ping might be firewalled, so check a webpage then
curl -Hfs www.google.com >/dev/null 2>&1 || return 1
fi
return 0
}
In real practices, I do not use the function (1), because it rarely useful simply to check if there is an interface UP. However I do use funtions (2) + (3):
The function (2) _isNetworkUp is called periodically in a script just to show whether my computer is networked
The function (3) _isInternetConnected is called in a script before doing something with internet, like sync computer time and check email. Because the function (3) costs more time and resources than function (2), so it is not suitable for periodically usage of short time interval. curl is more costly than ping; so ping first, then curl later.
(sorry if my English is lousy).
Examples:
## if Internet is connected, then sync time
## with world time server
if _isInternetConnected; then
sudo ntpdate pool.ntp.org
sudo hwclock --systohc --utc
fi
Explanations:
grep -l : grep stops immediate after 1st found.
grep -v : grep exclusively.
ping -c 4: ping only 4 times.

Related

the bash script only reboot the router without echoing whether it is up or down

#!/bin/bash
ip route add 10.105.8.100 via 192.168.1.100
date
cat /home/xxx/Documents/list.txt | while read output
do
ping="ping -c 3 -w 3 -q 'output'"
if $ping | grep -E "min/avg/max/mdev" > /dev/null; then
echo 'connection is ok'
else
echo "router $output is down"
then
cat /home/xxx/Documents/roots.txt | while read outputs
do
cd /home/xxx/Documents/routers
php rebootRouter.php "outputs" admin admin
done
fi
done
The other documents are:
lists.txt
10.105.8.100
roots.txt
192.168.1.100
when i run the script, the result is a reboot of the router am trying to ping. It doesn't ping.
Is there a problem with the bash script.??
If your files only contain a single line, there's no need for the while-loop, just use read:
read -r router_addr < /home/xxx/Documents/list.txt
# the grep is unnecessary, the return-code of the ping will be non-zero if the host is down
if ping -c 3 -w 3 -q "$router_addr" &> /dev/null; then
echo "connection to $router_addr is ok"
else
echo "router $router_addr is down"
read -r outputs < /home/xxx/Documents/roots.txt
cd /home/xxx/Documents/routers
php rebootRouter.php "$outputs" admin admin
fi
If your files contain multiple lines, you should redirect the file from the right-side of the while-loop:
while read -r output; do
...
done < /foo/bar/baz
Also make sure your files contain a newline at the end, or use the following pattern in your while-loops:
while read -r output || [[ -n $output ]]; do
...
done < /foo/bar/baz
where || [[ -n $output ]] is true even if the file doesn't end in a newline.
Note that the way you're checking for your routers status is somewhat brittle as even a single missed ping will force it to reboot (for example the checking computer returns from a sleep-state just as the script is running, the ping fails as the network is still down but the admin script succeeds as the network just comes up at that time).

output of dns-sd browse command not redirected to file in busybox shell(ash)

To check if mdnsd is in probing mode we are using below command to browse for service and redirect its output a file, and hostname of the device is found in the command we decide that mdnsd is in probing mode.
command used for publishing service
dns-sd -R "Test status" "_mytest._tcp." "local." "22"
To browse the service following command is used (Running in background)
dns-sd -lo -Z _mytest._tcp > /tmp/myfile &
To display the content of the file used cat.
cat /tmp/myfile
myfile is empty, if > replaced with tee , I see output on console myfile remains empty.
I am unable to understand what is going on.
Is there any pointer, help
EDIT
Just for completeness adding output, which i missed adding before.
# dns-sd -lo -Z _mytest._tcp local
Using LocalOnly
Using interface -1
Browsing for _mytest._tcp
DATE: ---Tue 25 Apr 2017---
11:09:24.775 ...STARTING...
; To direct clients to browse a different domain, substitute that domain in place of '#'
lb._dns-sd._udp PTR #
; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.
; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local
; names with the correct fully-qualified (unicast) domain name of the target host offering the service.
_mytest._tcp PTR Test\032status._mytest._tcp
Test\032status._mytest._tcp SRV 0 0 22 DevBoard.local. ; Replace with unicast FQDN of target host
Test\032status._mytest._tcp TXT ""
You appear to have a program with behavior that differs based on whether its output is to a TTY. One workaround is to use a tool such as unbuffer or script to simulate a TTY.
Moreover, inasmuch as the use of a file at all is done as a workaround, I suggest using a FIFO to actually capture the line you want without needing to write to a file and poll that file's contents.
#!/bin/sh
newline='
'
# Let's define some helpers...
cleanup() {
[ -e /proc/self/fd/3 ] && exec 3<&- ## close FD 3 if it's open
rm -f "fifo.$$" ## delete the FIFO from disk
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then ## if our pid is still running...
kill "$pid" ## ...then shut it down.
fi
}
die() { cleanup; echo "$*" >&2; exit 1; }
# Create a FIFO, and start `dns-sd` in the background *thinking* it's writing to a TTY
# but actually writing to that FIFO
mkfifo "fifo.$$"
script -q -f -c 'dns-sd -lo -Z _mytest._tcp local' /proc/self/fd/1 |
tr -d '\r' >"fifo.$$" & pid=$!
exec 3<"fifo.$$"
while read -t 1 -r line <&3; do
case $line in
"Script started on"*|";"*|"") continue;;
"Using "*|DATE:*|[[:digit:]]*) continue;;
*) result="${result}${line}${newline}"; break
esac
done
if [ -z "$result" ]; then
die "Timeout before receiving a non-boilerplate line"
fi
printf '%s\n' "Retrieved a result:" "$result"
cleanup

Test if name resolv with ping (or better than that)

I have a bash function:
resolv(){
for name in $*; do
test 1 -eq $(ping -c 1 -q "$name"|grep '1 packet transmitted'|wc -l) && echo ok || echo fail
done
}
But if I pass a name that is not defined it returns, always:
ping: unknown host name.that.do.not.resolv
fail
How can I solve this?
You could try getent which follows the /etc/nsswitch.conf config:
> getent hosts my_laptop
127.0.0.1 localhost my_laptop
> getent hosts www.google.com
2607:f8b0:400b:80a::1014 www.google.com
> getent hosts name.that.do.not.resolv
>
Sticking with the assumption you need to use ping (because, for example, of the broken^H^H^Hconfused dns resolution in OSX):
Note that your grep wasn't portable - some pings say "packet" and others say "packets"... within the scope of the ping's I had available, ^PING works though.
For your specific question, you wanted to suppress the error output - so just redirect stderr to /dev/null - if it's sh/bash/similar that'd just be 2>/dev/null
Also added -t1 to reduce the amount of time we might spend on a ping fail/lookup fail...might need to tune to suit.
Broke up the test && || into something more readable (and with more obvious logic direction) since we arent really constrained to a oneliner. :)
#!/bin/sh
resolv(){
for name in $*; do
count=$(ping -c 1 -q -t1 "$name" 2>/dev/null |grep '^PING'|wc -l)
if [ 1 -eq "$count" ]
then
echo ok
else
echo fail
fi
done
}
resolv google.com
resolv icantclick.org
resolv www.yahoo.com
resolv gofish

Modify shell script to monitor/ping multiple ip addresses

Alright so I need to constantly monitor multiple routers and computers, to make sure they remain online. I have found a great script here that will notify me via growl(so i can get instant notifications on my phone) if a single ip cannot be pinged. I have been attempting to modify the script to ping multiple addresses, with little luck. I'm having trouble trying to figure out how to ping a down server while the script keeps watching the online servers. any help would be greatly appreciated. I haven't done much shell scripting so this is quite new to me.
Thanks
#!/bin/sh
#Growl my Router alive!
#2010 by zionthelion73 [at] gmail . com
#use it for free
#redistribute or modify but keep these comments
#not for commercial purposes
iconpath="/path/to/router/icon/file/internet.png"
# path must be absolute or in "./path" form but relative to growlnotify position
# document icon is used, not document content
# Put the IP address of your router here
localip=192.168.1.1
clear
echo 'Router avaiability notification with Growl'
#variable
avaiable=false
com="################"
#comment prefix for logging porpouse
while true;
do
if $avaiable
then
echo "$com 1) $localip avaiable $com"
echo "1"
while ping -c 1 -t 2 $localip
do
sleep 5
done
growlnotify -s -I $iconpath -m "$localip is offline"
avaiable=false
else
echo "$com 2) $localip not avaiable $com"
#try to ping the router untill it come back and notify it
while !(ping -c 1 -t 2 $localip)
do
echo "$com trying.... $com"
sleep 5
done
echo "$com found $localip $com"
growlnotify -s -I $iconpath -m "$localip is online"
avaiable=true
fi
sleep 5
done
The simplest approach is to wrap this script with another one that creates N processes. Assume your script is called "watchip", then put into another script the text
watchip 10.0.1.1 &
watchip 10.0.1.2 &
watchip 10.0.1.3 &
etc
and set localip to $1 inside watchip.
I don't think it's necessary to run multiple scripts. Here is a general script to monitor a list of IP addresses and note changes in ping success...
#!/bin/bash
set 10.0.0.1 10.0.0.2 # etc
trap exit 2
while true; do
i=1
for ipnumber in "$#"; do
statusname=up$i
laststatus=${!statusname:-0}
ping -c 1 -t 2 $ipnumber > /dev/null
ok=$?
eval $statusname=$ok
if [ ${!statusname} -ne $laststatus ]; then
echo status changed for $ipnumber
if [ $ok -eq 0 ]; then
echo now it is up
else
echo now it is down
fi
fi
i=$(($i + 1))
done
sleep 5
done
Change localip=192.168.1.1 to:
localip=$1
This allows the IP address to be passed in as a command-line argument. Then you can run multiple copies of the script passing in different IP addresses. You could then create a master script to run multiple copies of the monitoring script. Assuming the script you posted is monitor.sh:
#!/bin/sh
monitor.sh 192.168.1.1 &
monitor.sh 192.168.2.2 &
monitor.sh 192.168.3.3 &
wait
Keep two arrays. One with available IPs; the other with unavailable ones. When their status changes, move them to the other array. No need for multiple background processes.
I've omitted the logging stuff. You can add it back in. This is untested code.
available=(192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4)
unavailable=()
while true
do
for index in ${!available[#]}
do
if ! ping -c 1 -t 2 ${available[index]}
then
growlnotify -s -I $iconpath -m "${available[index]} is offline"
unavailable+=(${available[index]})
unset "available[index]"
fi
done
for index in ${!unavailable[#]}
do
if ping -c 1 -t 2 ${unavailable[index]}
then
growlnotify -s -I $iconpath -m "${unavailable[index]} is back online"
available+=(${unavailable[index]})
unset "unavailable[index]"
fi
done
done

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?
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
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
This works on both MacOSX and Linux:
#!/bin/bash
ping -q -c1 google.com &>/dev/null && echo online || echo offline
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
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.
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
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?
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.
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
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
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:
It's possible for your computer to be connected to a network without that network having internet access
It's possible for a server to be down without the entire internet being inaccessible
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:
It is robust against all the false positives and negatives I outlined above
The requests all happen in parallel to maximize speed
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 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

Resources