kurtcms.org

Thinking. Writing. Philosophising.

Email Github LinkedIn

IP Networking: Ping Output to Comma Separated Values (CSV) at Runtime

Posted on April 19, 2021 — 5 Minutes Read

At times one in the field of IP networking will inevitably come across projects that require manipulating and analysing output from network performance measurement tools such as ping, traceroute and iPerf to name a few. Some of these tools were built with readability as well as computability in mind and as such were designed by default to print output friendly to the human eyes, with an option to convert the output at runtime to Comma Separated Values (CSV), should it need to be redirected to a file or a downstream function for manipulation and analysis. Some however were not. Prime among those is ping which, considering it is the go-to tool for checking network Round Trip time (RTT) and as such one of the most essential utilities in networking, is all the more surprising. That said, converting at runtime the standard human-readable ping output to a more processable CSV is simply a matter of coding a wrapper Bash script to filter and manipulate the result.

The rest of the code is shared on Github for reference and further development.

Writing a Bash script

There are many tools or scripting languages that one can use to automate tasks on a server and Bash is one of them. It is one of the most widely-used scripting languages and is the default login shell for Linux. When you connect to your Linux server by Secure Shell (SSH, RFC 4523), you are already using Bash.

Use the nano text editor to open a new file and start writing the script.

$ nano

First line to add the script shall be to inform the executer that this is a Bash script and is to be interpreted with Bash. This line is elegantly called a shebang.

#!/bin/bash

Thereafter goes the script.

#!/bin/bash

trap echo 0

ping $* | while read line; do

  [[ "$line" =~ ^PING ]] && continue
  [[ ! "$line" =~ "bytes from" ]] && continue

  dt=$(date +'%Y%m%d%H%M%S')
  et=$(date +%s)

  addr=${line##*bytes from }
  addr=${addr%%:*}

  rtt=${line##*time=}
  rtt=${rtt%% *}

  echo -n "$dt,$et,$addr,$rtt"
  echo
done

Control + o to save the script. Enter the a name for the new file e.g. pingc, with a full path if it is to be saved in a location other than the current working directory e.g. /app/pingc/pingc.sh. Press enter to save.

Control + x to exit nano.

Ping with a Loop

At its core this script executes the ping command with the rest of the argument supplied to the script. The rest of the output is then pipelined to a while loop instructed to read it line by line and perform the filtering and manipulation as appropriate.

ping $* | while read line; do
  ...
done

Five operations are required in total and the first of which is to filter the header and footer lines from the standard ping output that are not RTT results from the ICMP packets.

ping $* | while read line; do

  [[ "$line" =~ ^PING ]] && continue
  [[ ! "$line" =~ "bytes from" ]] && continue
  ...
done

Date and Time in Two Perspectives

Following the filtering are two commands that construct the date and time in two different formats. Each of which has its own merit and both will prove handy comes data manipulation and analysis. First of the two is the date and time in the numerical format of YYYYMMDDHHMMSS e.g. 20210419024510 that strikes a balance between comprehensibility and operability, and the second is the number of seconds since the epoch that is since 1970-01-01 00:00:00 UTC which will be a rather handy numerical counter in addition to the embedded date and time value in relation to the epoch.

ping $* | while read line; do

  ...
  dt=$(date +'%Y%m%d%H%M%S')
  et=$(date +%s)
  ...
done

Extracting Destination IP and RTT

With the date and time set, all is left is to output the two most important pieces of information from the ping namely the destination IP address and its RTT. Both of which are printed albeit as unstructured text by the standard ping output, and the Bash script will simply need to extract them accordingly.

ping $* | while read line; do

  ...
  addr=${line##*bytes from }
  addr=${addr%%:*}

  rtt=${line##*time=}
  rtt=${rtt%% *}
  ...
done

Echoing the Outputs

At the end of the while loop, before moving on and reading the next line, the rest of the above will be printed out with the tried and trusted echo command followed by an empty echo to ensure the next print will be on a new line.

ping $* | while read line; do

  ...
  echo -n "$dt,$et,$addr,$rtt"
  echo
done

Testing and Troubleshooting

Now the script can be tested.

First thing is to do is to provide the script with execute permission.

chmod +x *full-path-to-script*

$ chmod +x /app/pingc/pingc.sh

To execute the script, simply call it in your Bash shell with a destination IP as required by the ping command e.g. the Cloudflare DNS server at 1.1.1.1 and other optional argument(s) e.g. -c5 to instruct ping to stop after 5 counts.

$ /app/pingc/pingc.sh -c5 1.1.1.1

If all goes well, you should see an output similar to the below

20210419024510,1618800310,1.1.1.1,3.62
20210419024511,1618800311,1.1.1.1,3.36
20210419024512,1618800312,1.1.1.1,2.84
20210419024513,1618800313,1.1.1.1,3.38
20210419024514,1618800314,1.1.1.1,3.38

Below are some of the common errors.

Bash: permission denied

-bash: full-path-to-script: Permission denied

Please make sure that your script has execute permission. To add execute permission.

chmod +x *full-path-to-script*

$ chmod +x /app/pingc/pingc.sh

ping: command not found

full-path-to-script: line n: ping: command not found

Please make sure ping is installed on your server.

$ apt install iputils-ping

Thoughts

This is just one example of using a simple Bash script to manipulate command output. There are plenty of other fascinating tasks that can be done with Bash. Explore and be amazed.