tl;dr? Jump straight to the solution.

The Problem

I was recently having some network trouble, and was trying all sorts of things to fix it. (It turns out to be from a design flaw in my computer (“MacBook Pro (Retina, 13-inch, Mid 2014)”), where plugging in too many cables interferes with the wireless.) While I was trying different things to fix it, I was running ping, to see if the network was reachable, and how good the connection was.

At a glance, though, it wasn’t that easy to tell from the numerical output of ping whether the connection was good or bad. It was just a lot of numbers with no frame of reference. I needed a way to see how they were changing over time. I needed a graph.

Experimenting

An initial web search for a live ping graph turned up a couple of commercial GUI applications, but not much else. This was no good. Surely there was some way to get this in my Terminal?

Let’s break the problem down. Can we draw graphs in Terminal? Yes. But gnuplot can’t graph live data. However, there is a program called feedgnuplot that can read data from stdin, and call gnuplot over and over to make the graphs.

Let’s see if I can install feedgnuplot from Homebrew. My go-to way of checking this is just to try to run the command I want and see if homebrew-command-not-found comes up with any results. But, for feedgnuplot, there was nothing. Let’s try brew search feedgnuplot just in case. Still nothing. Ugh.

Well, looks like we’re installing from source. At least the installation is pretty straightforward.

Now we need the data to pass into feedgnuplot. We’re just interested in the time for each packet, so we need to extract that from the output of ping, which looks like this:

PING 192.168.0.1 (192.168.0.1): 56 data bytes
64 bytes from 192.168.0.1: icmp_seq=0 ttl=64 time=2.241 ms
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=3.930 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=2.776 ms

We can extract just that time column by cuting on the spaces and picking out the right row:

ping 192.168.0.1 | cut -d ' ' -f 7

And now, if we cut again, we can just extract the bit after the equals:

ping 192.168.0.1 | cut -d ' ' -f 7 | cut -d = -f 2

Oh… where did all the output go? I run this command, and it runs, but there’s no output, like just running cat. That’s strange. Is the first cut behaving differently because it’s being piped now? We can test this by piping into cat:

ping 192.168.0.1 | cut -d ' ' -f 7 | cat

Still no output. Why would cut suddenly become silent when it went into a pipe?

I got stuck here for a while, but eventually came across the solution: cut’s output was being buffered, so it would be coming out in huge spurts spaced pretty far apart, which wasn’t very useful for the live graph I wanted to build. So, apparently, to get live pipes working in UNIX, I need to stick another command ⏤ unbuffer -p in front of every command that isn’t at the start or end of the pipe chain, to trick them into thinking they’re being run in a different context.

This seems remarkably complicated and not-UNIX-y. unbuffer isn’t even installed on my system! To get it, I had to install a Homebrew Dupes package! At this point, I’m 100% convinced that this cannot be the correct way to do this. However, it does appear to be a way to do this, so let’s carry on.

Now I can get a live stream of ping response times:

ping 192.168.0.1 | unbuffer -p | cut -d ' ' -f 7 | cut -d = -f 2

We just need to pipe this into feedgnuplot. feedgnuplot has a lot of incomprehensible options, so I’ll just randomly copy some from the README and hope it works, remembering to add another unbuffer -p before the second cut, since it’s no longer the final command (sighs):

ping 192.168.0.1 | unbuffer -p cut -d ' ' -f 7 | unbuffer -p cut -d '=' -f 2 | feedgnuplot --lines --stream --terminal 'dumb 80,24'

Remarkably, this is almost exactly what I’m looking for:

    5 +-+------+--------+-------+--------+--------+--------+-------+------+-+   
      +        +        +       +        +        +        +       +        +   
      |        :        :       :        :        :        :       :        |   
  4.5 +-+...................**............................................+-+   
      |        :        :  *  **:        :        :        :       :        |   
      |        :        : *     *        :        :        :       :        |   
    4 +-+.**..............*......*........................................+-+   
      |     *  :        :*      : **     :        :        :       :        |   
      |      * :        *       :   *    :        :        :       :        |   
  3.5 +-+.....**.......*..................................................+-+   
      |        :*     * :       :    **  :        :        :       :        |   
      |        : *   *  :       :      * :        :        :       :        |   
      |        :  *  *  :       :       *:        :        :  ********      |   
    3 +-+...........*....................**.................**........**..+-+   
      |        :   *    :       :        : *      :       **       :    *   |   
      |        :        :       :        :  *     :     ** :       :        |   
  2.5 +-+.........................................******..................+-+   
      |        :        :       :        :   *****:        :       :        |   
      +        +        +       +        +        +        +       +        +   
    2 +-+------+--------+-------+--------+--------+--------+-------+------+-+   
      2        4        6       8        10       12       14      16       18  

It’s a bit difficult to differentiate the line graph from the gridlines, though, and it would be nice if the x-axis scale was a bit bigger so I could see far enough back in time for the data to be meaningful. After a good few minutes, which involves greping through the README for every possible variation on the word “grid” apart from “grid” itself, which doesn’t occur to me for some reason, I manage to accomplish both of these things:

ping 192.168.0.1 | unbuffer -p cut -d ' ' -f 7 | unbuffer -p cut -d '=' -f 2 | feedgnuplot --lines --stream --terminal 'dumb 80,24' --xlen 200 --extracmds 'unset grid'

Here’s the output:

  25 +-+---------+-----------------+-----------------+-----------------+*-+-+   
     |           +                 +                 +                 +* * |   
     |                                                 *                * * |   
     |                                                 *                * * |   
  20 +-+                                               *                * *-+   
     |  * *                                            *                * * |   
     |  * *                                            *                * * |   
     |  * *                                            *                * * |   
  15 +-+* *                                            *                * *-+   
     |  * *                      *                     *                * **|   
     |  * *               *      *   *             *   **              *** *|   
  10 +-+***               *      *   *             * * ** *            ***+*+   
     | * * *              *      *   *             * * ** *            *** *|   
     | * * **             *      *  **             *** ** * *          *** **   
     | * * **             **    **  * *            ****** * *          *** **   
   5 +-* * ***  *     *   **   *** ** *           * *********          ***+**   
     |** * * *  **    **  **   *** ** *           *  ********   *     **** *|   
     *** * * **** **** ***** ****** * *************  * **** **** * ******* *|   
     |     *  *  +*       * * *  **+     *        *  +           ***   *    |   
   0 +-+---------+-----------------+-----------------+-----------------+--+-+   
                 50               100               150               200       

The Solution

  1. Make sure you have all these commands available:
    • gnuplot
    • feedgnuplot
    • unbuffer
  2. Run this:

    ping 192.168.0.1 | unbuffer -p cut -d ' ' -f 7 | unbuffer -p cut -d '=' -f 2 | feedgnuplot --lines --stream --terminal 'dumb 80,24' --xlen 200 --extracmds 'unset grid'
    

A future project, if nobody else gets to it first, will be making a Homebrew package for feedgnuplot.