#!/bin/bash function display_help { cat << EOF ${0##*/} [-c column_index] [-e] [-f data_file] [-h] [-p] [-r refresh_rate_value] [-t column_title] -c column_index You can use this parameter several times and you must use it as many times than column_title parameter. column_index starts at one. -e Erase existing data file. Non-erase (append) mode by default. -f data_file data_file will contain data to be plotted. If you omit this parameter, a temporary data file (provided by mktemp command) will be used. -h This short help message. -p Enable data producer. Disabled by default. -r refresh_rate_value Refresh rate (in seconds) for plotting data. One second by default. -t column_title You can use this parameter several times and you must use it as many times than column_index parameter. Title used as name for plotting data. EOF } function parse_option { local current_option while [ ! $# -lt ${OPTIND} ] do while getopts :c:ef:hpr:t: current_option do case ${current_option} in c) g_column_container+=("${OPTARG}") ;; e) g_erase_data='true' ;; f) g_data_file=${OPTARG} ;; h) display_help exit ;; p) g_enable_producer='true' ;; r) g_period=${OPTARG} ;; t) g_title_container+=("${OPTARG}") ;; \?) echo "invalid option: -${OPTARG}" >&2 exit ;; esac done ((++OPTIND)) done } function consolide_option { [ -z "${g_data_file}" ] && { g_data_file=$(mktemp); g_enable_producer='true'; } g_period=${g_period:-1} g_column_container=("${g_column_container[@]:-4}") g_title_container=("${g_title_container[@]:-free}") g_enable_producer=${g_enable_producer:-false} g_erase_data=${g_erase_data:-false} [ "${g_enable_producer}" = 'false' ] && [ ! -f "${g_data_file}" ] && exit [ ${#g_column_container[@]} = ${#g_title_container[@]} ] || exit local idx=0 while [ ${idx} -lt ${#g_column_container[@]} ] do g_container+=("${g_column_container[idx]}") g_container+=("${g_title_container[idx]}") ((++idx)) done } function data_producer { local p_data_output_file=$1 local p_erase_data=$2 if [ "${p_erase_data}" = 'true' ] then vmstat -n 1 | sed -u '1,2d' > "${p_data_output_file}" else local word_count1=$(tail -2 "${p_data_output_file}" | sed -n '1p' | wc -w) local word_count2=$(tail -2 "${p_data_output_file}" | sed -n '2p' | wc -w) if [ ${word_count1} -ne ${word_count2} ] then local last_line=$(tail -1 "${p_data_output_file}") sed -i '$d' "${p_data_output_file}" echo '# corrupted data: '${last_line} >> "${p_data_output_file}" fi echo '# append data begin here' >> "${p_data_output_file}" vmstat -n 1 | sed -u '1,2d' >> "${p_data_output_file}" fi } function data_extractor { local p_data_input_file=$1 local p_data_output_file=$2 sed '$d' "${p_data_input_file}" | grep -v '^#' | nl -v0 > "${p_data_output_file}" } function compute_gnuplot_command { local p_data_input_file=$1 shift local p_data_column local p_data_title local gnuplot_command while [ $# -ne 0 ] do p_data_column=$1 ((++p_data_column)) shift p_data_title=$1 shift gnuplot_command+=', '\'${p_data_input_file}\'' using 1:'${p_data_column}' title '\'${p_data_title}\'' with lines' done gnuplot_command='plot'${gnuplot_command#,} echo "${gnuplot_command}" } function main { local p_data_file=$1 shift local p_refresh_period=$1 shift local p_enable_producer=$1 shift local p_erase_data=$1 shift trap 'kill -TERM -$$' INT [ "${p_enable_producer}" = 'true' ] && data_producer "${p_data_file}" "${p_erase_data}" & local tmp_data_file=$(mktemp) trap 'rm "'${tmp_data_file}'"' EXIT local gnuplot_command=$(compute_gnuplot_command "${tmp_data_file}" "$@") local previous_line_count=0 while true do sleep ${p_refresh_period} local current_line_count=$(wc -l "${p_data_file}" | awk '{print $1}') if [ ${previous_line_count} -ne ${current_line_count} ] then data_extractor "${p_data_file}" "${tmp_data_file}" echo "${gnuplot_command}" previous_line_count=${current_line_count} fi done } parse_option "$@" consolide_option main "${g_data_file}" "${g_period}" "${g_enable_producer}" "${g_erase_data}" "${g_container[@]}" | gnuplot -persistent ==== MORE ==== * https://marketplace.firefox.com/app/plotly * http://code.google.com/p/flot/# [[js]] JQuerY {{http://www.mastersinit.org/geeks-vs-nerds/geek-nerd.jpg}}