7

I wrote this bash script to have a notification when internet becomes accessible but I don't understand many of things that are happening. Here is the script:

while ! ping 8.8.8.8 -c 1 2&> /dev/null ; do true; done;
  • The -c 1 option tells ping that i want to send only one packet.
  • The 2&> /dev/null is there because i don't want to see output.
  • true is there because bash didn't accept the do; syntax.
  • the terminating condition is ! ping ... because ping returns non zero status code when no packet is received before some timeout.

The intended behavior was for this loop to end when ping succeeds and the terminal emulator will automatically send me a notification.


Problems

  • This command doesn't end even when internet becomes accessible.
  • Trying to kill the command with ctrl + C doesn't work. I think it's killing the sub-command ping instead of the whole command.
  • Only removing 2&> /dev/null makes every thing works except for the minor issue that it writes output to the terminal.

Questions

  1. What's wrong with my command?
  2. How exactly do terminal emulators react to ctrl + C?
  3. Why does removing 2&> /dev/null make it work?

Note that I already have a turnaround. Nevertheless I want to understand bash a little more.

f() { while ! ping 8.8.8.8 -c 1 ; do true; done; }; f 2&> /dev/null
Claudio
  • 9,427
  • 3
  • 28
  • 67
nawfel bgh
  • 953
  • 13
  • 26
  • 1
    my version of `ping` expects `ping -c 1 8.8.8.8` . Pluse-uno for exploring your problem! Good luck. – shellter Nov 23 '15 at 14:36

3 Answers3

8

The correct redirection operator is &>, not 2&>. The 2 is parsed as a separate argument to ping, and since pinging 2 never succeeds, the loop never exists.

chepner
  • 389,128
  • 51
  • 403
  • 529
  • * My fault. I failed to use this more verbose syntax `2>&1`. * You don't explain the `Ctrl + C` problem. Is `Ctrl+C` killing the the ping command instead of the loop? because with your suggestion (`&>`), the command is ctrl-c-interrupt-able when there is no network (when ping is failing). – nawfel bgh Nov 23 '15 at 15:22
  • 1
    Ctrl+C sends an interrupt to the current foreground job, which might be `ping`, or it might be the shell, depending on when you actually type it. – chepner Nov 23 '15 at 15:30
  • This doesn't really explain what's happening. After fixing the redirection issue, the commands becomes ctrl-c-interupptible. If you take into account that most time will be spent waiting the ttl to expire. So the probability that my Ctrl + C catches the brief moment when shell is executing is null. – nawfel bgh Nov 23 '15 at 16:07
  • Never mind. After changing 8.8.8.8 to a non used address in my local network. IT BECAME IMPOSSIBLE TO INTERRUPT the command with Ctrl + C. The previous behavior I was experiencing (of the command being interruptible when there is no internet) was because `ping` was returning instantaneously because the network was unreachable. Now that the target address is potentially reachable, `ping` waits for a complete TTL. Therefore, It becomes impossible to catch the very brief moment of bash running. – nawfel bgh Nov 23 '15 at 19:14
3

Something I usually do in that kind of loops is adding a sleep command instead of true:

while ! ping 8.8.8.8 -c 1 &> /dev/null ; do sleep 1; done;

In that way you can use Ctrl+C while in the sleep and cancel the whole loop.

FJRA
  • 306
  • 1
  • 6
  • Thanks but, apparently, `while ! ping 8.8.8.8 -c 1 &> /dev/null ; do true; done;` is interrupt-able, so I don't see a reason to use sleep. `true` is also shorter :p. – nawfel bgh Nov 23 '15 at 15:10
  • Yes, but it is less aggressive with your network :). – FJRA Nov 23 '15 at 16:36
  • See my comment on @chepner response. I realize now the utility of the sleep function. It increases the likelihood that it's bash that is running when Ctrl + C is clicked. – nawfel bgh Nov 23 '15 at 23:47
1

I think the better way is to setup Timeout in the ping.

while ! ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1 ; do sleep 1 ; done