Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ making the output prettier, more colorful, more compact, and easier to read.
showing the ping responses in a *graphical* way at the terminal (by using
colors and Unicode characters).

`prettyping` is written in `bash` and `awk`, and is reported to work on many
`prettyping` is written in `sh` and `awk`, and is reported to work on many
different systems (Linux, Mac OS X, BSD…), as well as running on different
versions of `awk` (`gawk`, `mawk`, `nawk`, `busybox awk`).

Expand All @@ -20,7 +20,7 @@ screenshots, videos at: <http://denilsonsa.github.io/prettyping/>
Requirements
------------

* `bash` (tested on 4.20, should work on versions as old as 2008)
* posix-compatible shell, e.g. `bash` or `dash`
* `awk` (either [gawk][], [mawk][], [nawk][] or [busybox awk][]; should work on
`gawk` versions as old as 2008; should probably work on any other awk
implementation)
Expand All @@ -35,7 +35,7 @@ Installation
2. Make it executable: `chmod +x prettyping`

That's all! No root permission is required. You can save and run it from any
directory. As long as your user can run `ping`, `bash` and `awk`, then
directory. As long as your user can run `ping`, `sh` and `awk`, then
`prettyping` will work.

Alternatively, you can download the latest tarball from GitHub: [![Latest release](https://img.shields.io/github/release/denilsonsa/prettyping.svg)](https://github.com/denilsonsa/prettyping/releases/latest)
Expand Down
15 changes: 11 additions & 4 deletions mockping.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
#
# This is just a mock ping program that reproduces the same output all the
# time. It is used for testing/developing prettyping.
Expand All @@ -8,6 +8,7 @@ sample_output() {
PING registro.br (200.160.2.3) 56(84) bytes of data.
Request timeout for icmp_seq 1
64 bytes from registro.br (200.160.2.3): icmp_seq=2 ttl=56 time=25.5 ms
SIGWINCH
64 bytes from registro.br (200.160.2.3): icmp_seq=3 ttl=56 time=55.7 ms
64 bytes from registro.br (200.160.2.3): icmp_seq=4 ttl=56 time=75.2 ms
ping: sendto: Network is down
Expand Down Expand Up @@ -49,7 +50,13 @@ rtt min/avg/max/mdev = 36.750/38.535/40.048/1.360 ms
EOF
}

sample_output | while read line; do
echo -E "$line"
sleep 0.25s
pgid=$(ps -o pgid= $$ | tr -d '[:space:]')

sample_output | while read -r line; do
if [ "$line" = "SIGWINCH" ]; then
kill -WINCH -- -"$pgid"
continue
fi
printf '%s\n' "$line"
sleep "${SLEEP_SECS:-0.25}"
done
290 changes: 138 additions & 152 deletions prettyping
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/sh
#
# Written by Denilson Figueiredo de Sá <denilsonsa@gmail.com>
# MIT license
Expand Down Expand Up @@ -78,172 +78,157 @@ All other parameters are passed directly to ping.
EOF
}

MYNAME=$(basename "$0")

# Thanks to people at #bash who pointed me at
# https://web.archive.org/web/20100301171512/https://bash-hackers.org/wiki/doku.php/scripting/posparams
parse_arguments() {
USE_COLOR=1
USE_MULTICOLOR=1
USE_UNICODE=1
USE_LEGEND=1
USE_GLOBALSTATS=1
USE_RECENTSTATS=1

if [ -t 1 ]; then
IS_TERMINAL=1
else
IS_TERMINAL=0
fi

LAST_N=60
OVERRIDE_COLUMNS=0
OVERRIDE_LINES=0
RTT_MIN=auto
RTT_MAX=auto

PING_BIN="ping"
#PING_BIN="./mockping.awk"
PING_PARAMS=( )

AWK_BIN="awk"
AWK_PARAMS=( )

while [[ $# != 0 ]] ; do
case "$1" in
-h | -help | --help )
print_help
exit
;;

# Forbidden ping parameters within prettyping:
-f )
echo "${MYNAME}: You can't use the -f (flood) option."
exit 1
;;
-R )
# -R prints extra information at each ping response.
echo "${MYNAME}: You can't use the -R (record route) option."
exit 1
;;
-q )
echo "${MYNAME}: You can't use the -q (quiet) option."
exit 1
;;
-v )
# -v enables verbose output. However, it seems the output with
# or without this option is the same. Anyway, prettyping will
# strip this parameter.
;;
# Note:
# Small values for -s parameter prevents ping from being able to
# calculate RTT.

# New parameters:
-a )
# TODO: Implement audible ping for responses or for missing packets
;;

-color | --color ) USE_COLOR=1 ;;
-nocolor | --nocolor ) USE_COLOR=0 ;;
-multicolor | --multicolor ) USE_MULTICOLOR=1 ;;
-nomulticolor | --nomulticolor ) USE_MULTICOLOR=0 ;;
-unicode | --unicode ) USE_UNICODE=1 ;;
-nounicode | --nounicode ) USE_UNICODE=0 ;;
-legend | --legend ) USE_LEGEND=1 ;;
-nolegend | --nolegend ) USE_LEGEND=0 ;;
-globalstats | --globalstats ) USE_GLOBALSTATS=1 ;;
-noglobalstats | --noglobalstats ) USE_GLOBALSTATS=0 ;;
-recentstats | --recentstats ) USE_RECENTSTATS=1 ;;
-norecentstats | --norecentstats ) USE_RECENTSTATS=0 ;;
-terminal | --terminal ) IS_TERMINAL=1 ;;
-noterminal | --noterminal ) IS_TERMINAL=0 ;;

-awkbin | --awkbin ) AWK_BIN="$2" ; shift ;;
-pingbin | --pingbin ) PING_BIN="$2" ; shift ;;

#TODO: Check if these parameters are numbers.
-last | --last ) LAST_N="$2" ; shift ;;
-columns | --columns ) OVERRIDE_COLUMNS="$2" ; shift ;;
-lines | --lines ) OVERRIDE_LINES="$2" ; shift ;;
-rttmin | --rttmin ) RTT_MIN="$2" ; shift ;;
-rttmax | --rttmax ) RTT_MAX="$2" ; shift ;;

* )
PING_PARAMS+=("$1")
;;
esac
USE_COLOR=1
USE_MULTICOLOR=1
USE_UNICODE=1
USE_LEGEND=1
USE_GLOBALSTATS=1
USE_RECENTSTATS=1

if [ -t 1 ]; then
IS_TERMINAL=1
else
IS_TERMINAL=0
fi

LAST_N=60
OVERRIDE_COLUMNS=0
OVERRIDE_LINES=0
RTT_MIN=0
RTT_MAX=0

PING_BIN="ping"
#PING_BIN="./mockping.sh"

AWK_BIN="awk"

skipnext=0
for arg; do
if [ "$skipnext" -eq 1 ]; then
skipnext=0
shift
done

if [[ "${RTT_MIN}" -gt 0 && "${RTT_MAX}" -gt 0 && "${RTT_MIN}" -ge "${RTT_MAX}" ]] ; then
echo "${MYNAME}: Invalid --rttmin and -rttmax values."
exit 1
continue
fi
case "$1" in
-h | -help | --help )
print_help
exit
;;

# Forbidden ping parameters within prettyping:
-f )
echo "${MYNAME}: You can't use the -f (flood) option."
exit 1
;;
-R )
# -R prints extra information at each ping response.
echo "${MYNAME}: You can't use the -R (record route) option."
exit 1
;;
-q )
echo "${MYNAME}: You can't use the -q (quiet) option."
exit 1
;;
-v )
# -v enables verbose output. However, it seems the output with
# or without this option is the same. Anyway, prettyping will
# strip this parameter.
;;
# Note:
# Small values for -s parameter prevents ping from being able to
# calculate RTT.

# New parameters:
-a )
# TODO: Implement audible ping for responses or for missing packets
;;

-color | --color ) USE_COLOR=1 ;;
-nocolor | --nocolor ) USE_COLOR=0 ;;
-multicolor | --multicolor ) USE_MULTICOLOR=1 ;;
-nomulticolor | --nomulticolor ) USE_MULTICOLOR=0 ;;
-unicode | --unicode ) USE_UNICODE=1 ;;
-nounicode | --nounicode ) USE_UNICODE=0 ;;
-legend | --legend ) USE_LEGEND=1 ;;
-nolegend | --nolegend ) USE_LEGEND=0 ;;
-globalstats | --globalstats ) USE_GLOBALSTATS=1 ;;
-noglobalstats | --noglobalstats ) USE_GLOBALSTATS=0 ;;
-recentstats | --recentstats ) USE_RECENTSTATS=1 ;;
-norecentstats | --norecentstats ) USE_RECENTSTATS=0 ;;
-terminal | --terminal ) IS_TERMINAL=1 ;;
-noterminal | --noterminal ) IS_TERMINAL=0 ;;

-awkbin | --awkbin ) AWK_BIN="$2" ; skipnext=1 ;;
-pingbin | --pingbin ) PING_BIN="$2" ; skipnext=1 ;;

#TODO: Check if these parameters are numbers.
-last | --last ) LAST_N="$2" ; skipnext=1 ;;
-columns | --columns ) OVERRIDE_COLUMNS="$2" ; skipnext=1 ;;
-lines | --lines ) OVERRIDE_LINES="$2" ; skipnext=1 ;;
-rttmin | --rttmin ) RTT_MIN="$2" ; skipnext=1 ;;
-rttmax | --rttmax ) RTT_MAX="$2" ; skipnext=1 ;;

* )
# unprocessed parameters will cycle to the front of $@ to be passed to `ping`
set -- "$@" "$1"
;;
esac
shift
done

if [ "${RTT_MIN}" -gt 0 ] && [ "${RTT_MAX}" -gt 0 ] && [ "${RTT_MIN}" -ge "${RTT_MAX}" ] ; then
echo "${MYNAME}: Invalid --rttmin and -rttmax values."
exit 1
fi

if [ $# -eq 0 ] ; then
echo "${MYNAME}: Missing parameters, use --help for instructions."
exit 1
fi

# Workaround for mawk:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504
awk_version="$(echo | "${AWK_BIN}" -W version 2>&1)"
case "$awk_version" in
mawk*) AWK_PARAMS="-W interactive" ;;
esac

if [[ "${#PING_PARAMS[@]}" = 0 ]] ; then
echo "${MYNAME}: Missing parameters, use --help for instructions."
exit 1
fi

# Workaround for mawk:
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504
local version="$(echo | "${AWK_BIN}" -W version 2>&1)"
if [[ "${version}" == mawk* ]] ; then
AWK_PARAMS+=(-W interactive)
fi
}

MYNAME=`basename "$0"`
parse_arguments "$@"


export LC_ALL=C

# Warning! Ugly code ahead!
# The code is so ugly that the comments explaining it are
# bigger than the code itself!
#
# Suppose this:
#
# cmd_a | cmd_b &
#
# I need the PID of cmd_a. How can I get it?
# In bash, $! will give me the PID of cmd_b.
#
# So, I came up with this ugly solution: open a subshell, like this:
#
# (
# cmd_a &
# echo "This is the PID I want $!"
# wait
# ) | cmd_b

# Ignore SIGINT (Ctrl-C) downstream of ping, so the pipeline can cleanly finish when ping is interrupted.

# Ignore Ctrl+C here.
# If I don't do this, this shell script is killed before
# ping and gawk can finish their work.
trap '' 2
"${PING_BIN}" "$@" 2>&1 | (
trap '' INT

# Now the ugly code.
(
"${PING_BIN}" "${PING_PARAMS[@]}" &
PING_PID="$!"

# Commented out, because it looks like this line is not needed
#trap "kill -2 $PING_PID ; exit 1" 2 # Catch Ctrl+C here

wait
) 2>&1 | (
if [ "${IS_TERMINAL}" = 1 ]; then
# Print a message to notify the awk script about terminal size change.
trap "echo SIGWINCH" 28
trap 'sigwinch=1; echo SIGWINCH' WINCH
fi

# The trap must be in another subshell because otherwise it will interrupt
# the "wait" commmand.
while read line; do
echo -E "$line"
sigwinch=0
while true; do
if ! read -r line; then
# some shells will interrupt read for the SIGWINCH, in which case we need to restart it
if [ "$sigwinch" -eq 1 ]; then
sigwinch=0
continue
else
break
fi
fi
printf '%s\n' "$line"
done
) 2>&1 | "${AWK_BIN}" "${AWK_PARAMS[@]}" '
) 2>&1 | (
trap '' INT

"${AWK_BIN}" ${AWK_PARAMS:+$AWK_PARAMS} '
# Weird that awk does not come with abs(), so I need to implement it.
function abs(x) {
return ( (x < 0) ? -x : x )
Expand Down Expand Up @@ -478,12 +463,12 @@ function print_received_response(rtt, block_index) {
} else {
block_index = 1 + int((rtt - BLOCK_RTT_MIN) * (BLOCK_LEN - 2) / BLOCK_RTT_RANGE)
}
printf( BLOCK[block_index] )
printf( BLOCK[block_index] ESC_DEFAULT )
CURR_COL++
}

function print_missing_response(rtt) {
printf( ESC_RED "!" )
printf( ESC_RED "!" ESC_DEFAULT )
CURR_COL++
}

Expand Down Expand Up @@ -889,3 +874,4 @@ BEGIN {
# Not needed when the output is a terminal, but does not hurt either.
fflush()
}'
)