UPDATE: This script is now on gist: https://gist.github.com/dgulino/4750139
Years ago I created a simple Python script to plot a list of numbers in a simple ASCII line graph. I use this script all the time; I extract (with awk,perl,
pyliner,...) a column of numbers out of a log file and pipe it in to this script. Very useful since even to today most admin work is done in a character based terminal.
UPDATE (4/28/2011): Also check my other terminal console escript entries:
runavg.escript,
escript to beam
All numbers are rounded to an integer. It auto resizes new graph entries when a new max is set (default max is 0), and plots a new max line in
bold. If you set a threshold, it outputs any line over that threshold in
red. Or
both.
I created an Erlang escript version of it, just as an exercise. It requires 'tput' to be in the path, this even works with the cygwin version. It also uses
An Erlang version of getopt. I didn't bother to install it, I just copied
getopt.erl to my src dir and compiled it. Also
chmod u+x
tgraph.escript
It's called as part of a pipe:
$ cat test.txt | ./tgraph.escript -t 40 -c 40
or with the file as a parameter:
./tgraph.escript test.txt -t 40 -c 40
test.txt:
1
50
20
3
45
34.0
12
0
1000
0
100
34
Output:
****************************************1
****************************************50
****************20
**3
************************************45
***************************34
**********12
0
****************************************1000
0
****100
*34
tgraph.escript:
#!/usr/bin/env escript
%% -*- erlang -*-
%%! -smp disable
%% Author: Drew Gulino
-module(tgraph).
-export([main/1]).
main(CmdLine) ->
OptSpecList = option_spec_list(),
case getopt:parse(OptSpecList, CmdLine) of
{ok, {Options, NonOptArgs}} ->
true;
{error, {Reason, Data}} ->
Options = [],
NonOptArgs = [],
io:format("Error: ~s ~p~n~n", [Reason, Data]),
version(),
getopt:usage(OptSpecList, "tgraph")
end,
Symbol = get_opt_value(symbol,Options),
Columns = get_opt_value(columns,Options),
Display_number = get_opt_value(display_number,Options),
Threshold = get_opt_value(threshold,Options),
Maximum = get_opt_value(maximum,Options),
Bold = strip_newlines(os:cmd("tput bold")),
Init = strip_newlines(os:cmd("tput init")),
Dim = strip_newlines(os:cmd("tput sgr0")),
Red = strip_newlines(os:cmd("tput setaf 1")),
%Green = strip_newlines(os:cmd("tput setaf 2")),
%Yellow = strip_newlines(os:cmd("tput setaf 3")),
%Blue = strip_newlines(os:cmd("tput setaf 4")),
%Magenta = strip_newlines(os:cmd("tput setaf 5")),
case NonOptArgs of
[] ->
F = standard_io;
_ ->
{ok, F} = file:open(NonOptArgs, read)
end,
proc_file(F, {Symbol, Columns, Display_number, Threshold, Maximum} , {Bold, Init, Dim, Red}).
version() ->
io:format("Version: 1.1\n").
get_opt_value(Key, Options) ->
case lists:keyfind(Key,1,Options) of
{Key, Value} ->
Value
end,
Value.
option_spec_list() ->
%CurrentUser = os:getenv("USER"),
[
%% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg}
{help, $h, "help", undefined, "Show the program options"},
{version, $v, "version", undefined, "Version"},
{display_number, $n, "display_number", {boolean, true}, "Display number w/graph"},
{columns, $c, "columns", {integer, 72}, "Display columns (default = 72)"},
{symbol, $s, "symbol", {string, "*"}, "Symbol to display (default = '*')"},
{threshold, $t, "threshold", {integer, 0}, "Will color lines over this value"},
{maximum, $m, "maximum", {integer, 0}, "Presets the scale for this maximum value (default = 0)"}
].
proc_file(F, Options, Tput) ->
{Symbol, Columns, Display_number, Threshold, Maximum} = Options,
{Bold, Init, Dim, Red} = Tput,
%Columns = erlang:list_to_integer(os:cmd("tput cols")) - 8},
L = io:get_line(F, ''),
case L of
eof ->
ok;
"\n" ->
false;
Line ->
Stripped = strip_newlines(Line),
Num = cast_to_integer(Stripped),
io:put_chars(Init),
case Num > 0 of
true ->
case Num >= Maximum of
true ->
NewMax = Num,
io:put_chars(Bold);
false ->
NewMax = Maximum,
io:put_chars(Dim)
end,
Scale = Columns / NewMax,
Graph = lists:map(fun(_) -> io_lib:format(Symbol,[]) end , lists:seq(1,erlang:round(Num * Scale))),
case Threshold of
0 ->
false;
_ ->
case Num >= Threshold of
true ->
io:put_chars(Red);
false ->
%io:put_chars(Init)
false
end
end,
case Display_number of
true ->
io:format("~s~p~n",[Graph,Num]);
false ->
io:format("~p~n",[Graph])
end,
NewOptions = {Symbol, Columns, Display_number, Threshold, NewMax},
proc_file(F,NewOptions, Tput);
false ->
io:put_chars(Dim),
io:put_chars(Init),
io:format("~p~n",[Num]),
proc_file(F,Options, Tput)
end
end.
strip_newlines(String) ->
string:strip(re:replace(String,"(.*)[\n\r]","\\1", [global,{return,list}])).
cast_to_integer([]) ->
[];
cast_to_integer(Input) when is_integer(Input) ->
Input;
cast_to_integer(Input) when is_float(Input) ->
erlang:round(Input);
cast_to_integer(Input) when is_list(Input)->
case lists:member($., Input) of
true ->
erlang:round(erlang:list_to_float(Input));
false ->
erlang:list_to_integer(Input)
end.
UPDATE (4/26/2001):
Here's a link to the original python script:
tgraph.py
UPDATE (4/27/2011):
Version 1.1:
1) Fixed bug where lines were never dimmed after being bolded in cygwin
2) Changed the compiler flags to disable smp and not register the process name. Both not needed. One note: Couldn't get +Bc to work in Windows. This should change the break key to Ctrl-C, but still stays Ctrl-Break.
3) Moved tput initialization out of working loop, now runs quickly.