4

I am reading "Advanced Bash-Scripting Guide" at the moment. There is some script that not working correctly on my machine:

HNAME=news-15.net # Notorious spammer.
# HNAME=$HOST
# Debug: test for localhost.
count=2 # Send only two pings.
if [[ `ping -c $count "$HNAME"` ]]
then
echo ""$HNAME" still up and broadcasting spam your way."
else
echo ""$HNAME" seems to be down. Pity."
fi

It always prints $HNAME" still up and broadcasting spam your way. - even with non-existing IP addresses. Can someone clarify what is the problem?

It works correctly when I use a hostname instead of an IP address.

  • 1
    I got news-15.net seems to be down. Pity. – Siva Aug 16 '18 at 11:50
  • Strange. What's the exit code of a ping "$HNAME"? – RudiC Aug 16 '18 at 11:53
  • I've just added this 2 lines to the script just before the test statement. ping -c $count "$HNAME" > /dev/null 2>&1 echo "ping exit code is $?" # return 1 It print 1 with nonexistent ip... But in the if statement it prints out "$HNAME up " – artemmiet Aug 16 '18 at 13:44
  • 2
    Can you make the title of the question more specific, please? Nearly all questions tagged "bash" could have the title "Bash script does not work" :) – psmears Aug 16 '18 at 15:28
  • 1
    @psmears I often find myself submitting edits to question titles for this very reason. The Catch-22 of it is that the person asking the question tends not to know a good way to phrase it until the problem is identified. I just took a shot at it, and see if this passes peer review. – Monty Harder Aug 16 '18 at 17:52

2 Answers2

10
if [[ `ping -c $count "$HNAME"` ]]

This runs ping in a command substitution (using the old backtick syntax, instead of the saner $(...)). The resulting output is put on the command line, and then [[ .. ]], tests to see if the output is empty. (That is, regular output. Command substitution doesn't capture error output, which would still be sent to the script's error output.)

If ping outputs anything, the test succeeds. E.g. on my system:

$ ping -c1 1.2.3.4 2>/dev/null
PING 1.2.3.4 (1.2.3.4) 56(84) bytes of data.

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

Since 1.2.3.4 is a valid IP address, ping tries to contact it. With an invalid IP address or a nonexisting hostname, it would only print an error, and the standard output would be empty.

A better way to test if a host is up would be to test the exit status of ping, and redirect its output away:

host=1.2.3.4
if ping -c1 "$host" >/dev/null 2>&1; then
    echo "'$host' is up"
else
    echo "'$host' does not respond (or invalid IP address/hostname)"
fi

Note that the quoting in the echo commands is off:

echo ""$HNAME" seems to be down."

This has an empty quoted string "", then an unquoted parameter expansion $HNAME, and then the quoted string " seems to be down.". For various reasons, it's better to quote all paremeter expansions, so use "$var blahblah", or "\"$var\" blahblah" if you want to put quotes around the variable in the output.

See:

ilkkachu
  • 138,973
3

As you mentioned in question even with nonexistent ip. If you pinging IP instead of a domain. You should try the below approach to get the desired result.

HNAME=192.168.1.21 
count=2 # Send only two pings.
if ping -c $count "$HNAME" 2> /dev/null
then
    echo "\"$HNAME\" still up and broadcasting spam your way."
else
    echo "\"$HNAME\" seems to be down. Pity."
fi
Siva
  • 9,077
  • 2
    From a pedagogical standpoint, it might be helpful to explain why this is different i.e. that it tests the exit status of the ping command, rather than relying on whether or not it produces anything on standard output – steeldriver Aug 16 '18 at 12:30
  • @АртёмГригорьев because ping is notoriously "chatty" - it outputs messages to stdout in all sorts of cases - not directly indicative of success/failure – steeldriver Aug 16 '18 at 14:01