I would like to generate a streamable histgram that runs in bash. Given an input stream of integers (from stdin or a file), I would like to transform each integer to that respective number of “#” up to the length of the terminal window; in other words, 5 would become “#####”, and so on.
You can get the maximum number of columns in your current terminal using the following command,
twarnock@laptop: :) tput cols 143
The first thing we’ll want to do is create a string of “####” that is exactly as long as the max number of columns. I.e.,
COLS=$(tput cols); MAX_HIST=`eval printf '\#%.0s' {1..$COLS}; echo;`
We can use the following syntax to print a substring of MAX_HIST to any given length (up to its maximum length).
twarnock@laptop: :) echo ${MAX_HIST:0:5} ##### twarnock@laptop: :) echo ${MAX_HIST:0:2} ## twarnock@laptop: :) echo ${MAX_HIST:0:15} ###############
We can then put this into a simple shell script, in this case printHIST.sh, as follows,
#! /bin/bash COLS=$(tput cols); MAX_HIST=`eval printf '\#%.0s' {1..$COLS}; echo;` while read datain do if [ -n "$datain" ]; then echo -n ${MAX_HIST:0:$datain} if [ $datain -gt $COLS ]; then printf "\r$datain\n" else printf "\n" fi fi done < "${1:-/dev/stdin}"
This script will also print any number on top of any line that is larger than the maximum number of columns in the terminal window.
As is, the script will transform an input file into a crude histogram, but I’ve also used it as a visual ping monitor as follows (note the use of unbuffer),
twarnock@cosmos:~ :) ping $remote_host | unbuffer -p awk -F'[ =]' '{ print int($10) }' | unbuffer -p printHIST.sh ###### ##### ######## ###### ## #### ################# ### ##### #######