2

I'm writing a shell script to run ping command in background, and in the same time, set a timeout value for ping command, I want to get the exit code if the ping is ended by timeout, I use below:

timeout 10s ping -c 5 www.google.com | awk '{print "from local to google |", $0;}' &
exit_status=$?
echo $exit_status

but I found that the exit code returned is for "&", the command run in background, not the timeout.

how could I get the timeout exit code?

Satō Katsura
  • 13,368
  • 2
  • 31
  • 50
Echo
  • 21

1 Answers1

2

There are a number of problems here. First, timeout takes as argument a single command. So when you write

timeout 10s ping -c 5 www.google.com | awk '{print "from local to google |", $0;}'

what happens is more or less this:

( timeout 10s ping -c 5 www.google.com ) | awk '{print "from local to google |", $0;}'

I.e. timeout kills ping, not the entire pipe, and thus you get back the exit code of the pipe (namely that of awk).

To get the exit code of timeout you need a subshell. But since timeout takes as argument a single command, you need to write that explicitly:

timeout 10 sh -c 'ping ... | awk ...' sh

Alternatively you could just time the last command in the pipe (i.e. awk):

ping ... | timeout 10 awk ...

This should work fine for ping | awk, but it can be prone to race conditions for other commands. And if ping produces a lot of output you might also may get a SIGPIPE, for killing the listener.

Back to your question, you also want to put all that in background. To get the exit code of a background command you need to wait for it:

timeout 10 sh -c 'ping ... | awk ...' sh &
wait $!
echo $?

Sadly bash doesn't have an asynchronous wait, which kind of defeats the purpose of running the commands in background. You can however run other commands before wait.

Satō Katsura
  • 13,368
  • 2
  • 31
  • 50
  • Thanks, much appreciate your answer. Actually I wanted to speed up the ping, I'm writing a script to run ping automatically between different hosts and for difference network. so I roughly counted that there gonna have more than 2000 ping, and each ping suppose to send 5 packages. so I was thinking to run the ping in background, so don't have to wait one ping finish then continue another. do you have any idea to speed up? thanks again. – Echo Jun 09 '17 at 03:01
  • The bottleneck for ping-ing thousands of hosts is likely to be the DNS, not ping itself. This is much better handled by dedicated monitoring programs, but if you must reinvent your own wheel you should probably write a Python script that does asynch DNS lookups, and sends ICMP echo packets in several threads. Doing it in the shell is definitely not the way to go. – Satō Katsura Jun 09 '17 at 06:24
  • thanks. that's a very good advice. let me start learn the python now...... – Echo Jun 09 '17 at 11:02