Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the percent of packets received from Ping in bash?

Tags:

linux

bash

ping

When pinging a host I want my output just to show the percentage of packets (5 sent) received. I assume I need to use grep somehow but I can't figure out how (I'm new to bash programming). Here is where I am: ping -c 5 -q $host | grep ?. What should go in grep? I think I will have to do some arithmetic to get the percent received but I can deal with that. How can I pull out the info I need from the summary that ping will output?

like image 399
austinminn Avatar asked Nov 29 '11 16:11

austinminn


People also ask

How many packets does ping send Linux?

The default size of packets sent by the ping command to hosts is 56 bytes.

Which statement correctly define the ping command?

The ping command is a Command Prompt command used to test the ability of the source computer to reach a specified destination computer.


1 Answers

So far we've got an answer using grep, sed, perl, bc, and bash. Here is one in the flavor of AWK, "an interpreted programming language designed for text processing". This approach is designed for watching/capturing real-time packet loss information using ping.

To see only packet loss information:

Command

$ ping google.com | awk '{ sent=NR-1; received+=/^.*(time=.+ ms).*$/; loss=0; } { if (sent>0) loss=100-((received/sent)*100) } { printf "sent:%d received:%d loss:%d%%\n", sent, received, loss }'

Output

sent:0 received:0 loss:0%
sent:1 received:1 loss:0%
sent:2 received:2 loss:0%
sent:3 received:2 loss:33%
sent:4 received:2 loss:50%
sent:5 received:3 loss:40%
^C

However, I find it useful to see the original input as well. For this you just add print $0; to the last block in the script:

Command

$ ping google.com | awk '{ sent=NR-1; received+=/^.*(time=.+ ms).*$/; loss=0; } { if (sent>0) loss=100-((received/sent)*100) } { print $0; printf "sent:%d received:%d loss:%d%%\n", sent, received, loss; }'

Output

PING google.com (173.194.33.104): 56 data bytes
sent:0 received:0 loss:0%
64 bytes from 173.194.33.46: icmp_seq=0 ttl=55 time=18.314 ms
sent:1 received:1 loss:0%
64 bytes from 173.194.33.46: icmp_seq=1 ttl=55 time=31.477 ms
sent:2 received:2 loss:0%
Request timeout for icmp_seq 2
sent:3 received:2 loss:33%
Request timeout for icmp_seq 3
sent:4 received:2 loss:50%
64 bytes from 173.194.33.46: icmp_seq=4 ttl=55 time=20.397 ms
sent:5 received:3 loss:40%
^C

How does this all work?

You read the command, tried it, and it works! So what exactly is happening?

$ ping google.com | awk '...'

We start by pinging google.com and piping the output into awk, the interpreter. Everything in single quotes defines the logic of our script.

Here it is in a whitespace friendly format:

# Gather Data
{
  sent=NR-1;
  received+=/^.*(time=.+ ms).*$/;
  loss=0;
}

# Calculate Loss
{
  if (sent>0) loss=100-((received/sent)*100)
}

# Output
{
  print $0; # remove this line if you don't want the original input displayed
  printf "sent:%d received:%d loss:%d%%\n", sent, received, loss;
}

We can break it down into three components:

{ gather data } { calculate loss } { output }

Each time ping outputs information, the AWK script will consume it and run this logic against it.

Gather Data

{ sent=NR-1; received+=/^.*(time=.+ ms).*$/; loss=0; }

This one has three actions; defining the sent, received, and loss variables.

sent=NR-1;

NR is an AWK variable for the current number of records. In AWK, a record corresponds to a line. In our case, a single line of output from ping. The first line of output from ping is a header and doesn't represent an actual ICMP request. So we create a variable, sent, and assign it the current line number minus one.

received+=/^.*(time=.+ ms).*$/;

Here we use a Regular Expresssion, ^.*(time=.+ ms).*$, to determine if the ICMP request was successful or not. Since every successful ping returns the length of time it took, we use that as our key.

For those that aren't great with regex patterns, this is what ours means:

  1. ^ starting at the beginning of the line
  2. .* match anything until the next rule
  3. (time=.+ ms) match "time=N ms", where N can be one or more of any character
  4. .* match anything until the next rule
  5. $ stop at the end of the line

When the pattern is matched, we increment the received variable.

Calculate Loss

{ if (sent>0) loss=100-((received/sent)*100) }

Now that we know how many ICMP requests were sent and received we can start doing the math to determine packet loss. To avoid a divide by zero error, we make sure a request has been sent before doing any calculations. The calculation itself is pretty simple:

  1. received/sent = percentage of success in decimal format
  2. *100 = convert from decimal to integer format
  3. 100- = invert the percentage from success to failure

Output

{ print $0; printf "sent:%d received:%d loss:%d%%\n", sent, received, loss; }

Finally we just need to print the relevant info.


I don't want to remember all of this

Instead of typing that out every time, or hunting down this answer, you can save the script to a file (e.g. packet_loss.awk). Then all you need to type is:

$ ping google.com | awk -f packet_loss.awk
like image 52
Exide Avatar answered Oct 17 '22 02:10

Exide