MOTDs32/bar

669 lines
21 KiB
Text
Raw Normal View History

2017-07-20 22:54:13 +02:00
#! /bin/sh
# bar
# 'cat' with ASCII progress bar
# (c) Henrik Theiling
BAR_VERSION=1.4
# Synopsis:
# 'bar' works just like 'cat', but shows a progress bar in ASCII art on stderr.
# The script's main function is meant to be usable in any Bourne shell to be
# suitable for install scripts without the need for any additional tool.
#
# Shell Script Usage: bar [options] [files]
# Options:
# -h displays help
# ...
#
# Examples:
# Normal pipe:
#
# : bar mypack.tar.bz2 | tar xjpf -
#
# Individual pipe for each file:
#
# : bar -c 'tar xjpf -' mypack1.tar.bz2 mypack2.tar.bz2
#
# Individual pipe, using ${bar_file} variable:
#
# : bar -c 'echo ${bar_file}: ; gzip -dc | tar tvf -' \
# : -e .tar.gz \
# : file1 file2 file3 file4 file5 \
# : > package-list.txt
#####################################################
# Programs and shell commands:
#
# Required (otherwise this fails):
#
# if, then, else, fi, expr, test, cat, eval, exec
# shell functions
#
# test:
# a = b
# a -lt b
# a -gt b
# a -le b
# -f a
# -n a
# -z a
#
# expr:
# a + b
# a - b
# a '*' b
# a / b
# a : b
#
# Optional (otherwise this does not show the bar):
#
# grep, dd, echo, ls, sed, cut
#
# ls:
# must output the file size at fifth position.
#
# The command line interface also uses:
#
# awk
#
####>-SCHNIPP-<########################################################
bar_cat()
{
# Use this shell function in your own install scripts.
#####################################################
# Options:
# Width of the bar (in ten characters). The default is 76 characters.
test -z "${BAR_WIDTH}" && test -n "${COLUMNS}" && BAR_WIDTH=${COLUMNS}
# Check syntax:
( expr "${BAR_WIDTH}" + 0 >/dev/null 2>&1 ) || BAR_WIDTH=0
BAR_WIDTH=`expr ${BAR_WIDTH} + 0` || BAR_WIDTH=0
test "x${BAR_WIDTH}" = x0 && BAR_WIDTH=76
# Maximal block size to use for dd.
test -n "${BAR_BS}" || BAR_BS=1048567
# BEGIN PERC
# Whether to show a percentage.
test -n "${BAR_PERC}" || BAR_PERC=1
# END PERC
# BEGIN ETA
# Whether to show estimated time of arrival (ETA).
test -n "${BAR_ETA}" || BAR_ETA=1
# END ETA
# Width of the trace display:
# BEGIN TRACE
test -n "${BAR_TRACE_WIDTH}" || BAR_TRACE_WIDTH=10
# END TRACE
# The command to execute for every given file. Each file
# is piped into this command individually. By default, the
# files are simply dumped to stdout.
test -n "${BAR_CMD}" || BAR_CMD=cat
# The characters to be used in the bar
test -n "${BAR_L}" || BAR_L='['
test -n "${BAR_R}" || BAR_R=']'
test -n "${BAR_C0}" || BAR_C0='.'
test -n "${BAR_C1}" || BAR_C1='='
# Additional extension to add to each file:
#BAR_EXT=${BAR_EXT-}
# Whether to clear bar after termination. Otherwise keep the full bar.
#BAR_CLEAR=${BAR_CLEAR-0}
# Unless switched off by user, use the bar by default:
test -n "${BAR_OK}" || BAR_OK=1
#####################################################
BAR_WIDTH=`expr ${BAR_WIDTH} - 3`
bar_trace=''
# BEGIN TRACE
if test "x${BAR_TRACE}" = x1
then
BAR_WIDTH=`expr ${BAR_WIDTH} - ${BAR_TRACE_WIDTH}`
bar_lauf=${BAR_TRACE_WIDTH}
bar_t_space=''
bar_t_dot=''
while test "${bar_lauf}" -gt 1
do
bar_t_space="${bar_t_space} "
bar_t_dot="${bar_t_dot}."
bar_lauf=`expr ${bar_lauf} - 1`
done
bar_trace="${bar_t_space} "
fi
# END TRACE
bar_eta=''
BAR_GET_TIME='echo'
# BEGIN ETA
( expr 1 + ${SECONDS} >/dev/null 2>&1 ) || BAR_ETA=0
if test "x${BAR_ETA}" = x1
then
BAR_GET_TIME='( echo ${SECONDS} )'
BAR_WIDTH=`expr ${BAR_WIDTH} - 6`
bar_eta='--:-- '
fi
# END ETA
bar_perc=''
# BEGIN PERC
if test "x${BAR_PERC}" = x1
then
BAR_WIDTH=`expr ${BAR_WIDTH} - 5`
bar_perc=' 0% '
fi
# END PERC
BAR_GET_SIZE='( ls -l "${BAR_DIR}${bar_file}${BAR_EXT}" | sed "s@ *@ @g" | cut -d " " -f 5 ) 2>/dev/null'
# portable?
# check features:
( ( echo a ) >/dev/null 2>&1 ) || BAR_OK=0
( ( echo a | dd bs=2 count=2 ) >/dev/null 2>&1 ) || BAR_OK=0
( ( echo a | grep a ) >/dev/null 2>&1 ) || BAR_OK=0
( ( echo a | sed 's@ *@ @g' ) >/dev/null 2>&1 ) || BAR_OK=0
( ( echo a | cut -d ' ' -f 1 ) >/dev/null 2>&1 ) || BAR_OK=0
# check ranges:
test "${BAR_WIDTH}" -ge 4 || BAR_OK=0
BAR_ECHO='echo'
BAR_E_C1=''
BAR_E_C2=''
BAR_E_NL='echo'
# Does echo accept -n without signalling an error?
if echo -n abc >/dev/null 2>&1
then
BAR_E_C1='-n'
fi
# Check how to print a line without newline:
if ( ( ${BAR_ECHO} "${BAR_E_C1}" abc ; echo 1,2,3 ) | grep n ) >/dev/null 2>&1
then
# Try echo \c:
if ( ( ${BAR_ECHO} 'xyz\c' ; echo 1,2,3 ) | grep c ) >/dev/null 2>&1
then
# Try printf:
if ( ( printf 'ab%s' c ; echo 1,2,3 ) | grep abc ) >/dev/null 2>&1
then
BAR_ECHO='printf'
BAR_E_C1='%s'
else
BAR_ECHO=':'
BAR_E_C1=''
BAR_E_NL=':'
BAR_OK=0
fi
else
BAR_E_C1=''
BAR_E_C2='\c'
fi
fi
# prepare initial bar:
bar_shown=0
if test "${BAR_OK}" = 1
then
bar_lauf=0
bar_graph=''
while test `expr ${bar_lauf} + 5` -le "${BAR_WIDTH}"
do
bar_graph="${bar_graph}${BAR_C0}${BAR_C0}${BAR_C0}${BAR_C0}${BAR_C0}"
bar_lauf=`expr ${bar_lauf} + 5`
done
while test "${bar_lauf}" -lt "${BAR_WIDTH}"
do
bar_graph="${bar_graph}${BAR_C0}"
bar_lauf=`expr ${bar_lauf} + 1`
done
${BAR_E_C2}" 1>&2_eta}${bar_perc}${BAR_L}${bar_graph}${BAR_R}
bar_shown=1
fi
# for shifting large numbers so that expr can handle them:
# Assume we can compute up to 2147483647, thus 9 arbitrary digits.
# We must be able to do + of two numbers of 9 digits length. Ok.
# BEGIN LARGE
( ( test 1999999998 = `expr 999999999 + 999999999` ) >/dev/null 2>&1 ) || BAR_OK=0
bar_large_num="........."
bar_div=""
# END LARGE
bar_numsuff=""
# find size:
bar_size=0
if test -n "${BAR_SIZE}"
then
bar_size=${BAR_SIZE}
# BEGIN LARGE
while expr "${bar_size}" : "${bar_large_num}" >/dev/null 2>&1
do
bar_div="${bar_div}."
bar_numsuff="${bar_numsuff}0"
bar_size=`expr "${bar_size}" : '\(.*\).$'`
done
# END LARGE
BAR_GET_SIZE="echo '${BAR_SIZE}'"
else
for bar_file
do
bar_size1=0
if test -f "${BAR_DIR}${bar_file}${BAR_EXT}"
then
bar_size1=`eval "${BAR_GET_SIZE}"`
# BEGIN LARGE
# divide and upround by pattern matching:
if test -n "${bar_div}"
then
bar_size1=`expr "${bar_size1}" : '\(.*\)'${bar_div}'$'` || bar_size1=0
fi
# adjust if still too large:
while expr "${bar_size1}" : "${bar_large_num}" >/dev/null 2>&1
do
bar_div="${bar_div}."
bar_numsuff="${bar_numsuff}0"
bar_size1=`expr "${bar_size1}" : '\(.*\).$'`
bar_size=`expr "${bar_size}" : '\(.*\).$'` || bar_size=0
done
# upround if necessary:
if test -n "${bar_div}"
then
bar_size1=`expr "${bar_size1}" + 1`
fi
# END LARGE
# add to total size:
bar_size=`expr ${bar_size} + ${bar_size1}`
# BEGIN LARGE
# adjust if still too large:
while expr "${bar_size}" : "${bar_large_num}" >/dev/null 2>&1
do
bar_div="${bar_div}."
bar_numsuff="${bar_numsuff}0"
bar_size=`expr "${bar_size}" : '\(.*\).$'`
done
# END LARGE
else
BAR_OK=0
fi
done
fi
bar_quad=`expr ${BAR_WIDTH} '*' ${BAR_WIDTH}`
test "${bar_size}" -gt "${bar_quad}" || BAR_OK=0
if test "${BAR_OK}" = 0
then
# For some reason, we cannot display the bar. Thus plain operation:
for bar_file
do
if test "${bar_file}" = "/dev/stdin"
then
eval "${BAR_CMD}"
else
eval "${BAR_CMD}" < "${BAR_DIR}${bar_file}${BAR_EXT}"
fi
done
else
# Compute wanted bytes per step:
bar_want_bps=`expr ${bar_size} + ${BAR_WIDTH}`
bar_want_bps=`expr ${bar_want_bps} - 1`
bar_want_bps=`expr ${bar_want_bps} / ${BAR_WIDTH}`
# Compute block count per step to keep within maximum block size:
bar_count=1
if test "${bar_want_bps}" -gt "${BAR_BS}"
then
bar_count=`expr ${bar_want_bps} + ${BAR_BS}`
bar_count=`expr ${bar_count} - 1`
bar_count=`expr ${bar_count} / ${BAR_BS}`
fi
# Compute block size for given count:
bar_wc=`expr ${BAR_WIDTH} '*' ${bar_count}`
bar_bs=`expr ${bar_size} + ${bar_wc}`
bar_bs=`expr ${bar_bs} - 1`
bar_bs=`expr ${bar_bs} / ${bar_wc}`
# Compute bs * count, the bytes per step:
bar_bps=`expr ${bar_bs} '*' ${bar_count}`
# Compute bytes per hundredth:
bar_bph=`expr ${bar_size} + 99`
bar_bph=`expr ${bar_bph} / 100`
# Run loop:
bar_pos=0
bar_graph="${BAR_L}"
bar_cur_char=0
bar_t0=`eval "${BAR_GET_TIME}" 2>/dev/null` || bar_t0=0
for bar_file
do
# BEGIN TRACE
if test "x${BAR_TRACE}" = x1
then
bar_trace=`expr "${bar_file}" : '.*/\([^/][^/]*\)$'` || bar_trace="${bar_file}"
bar_trace=`expr "${bar_trace}${bar_t_space}" : '\('${bar_t_dot}'\)'`
bar_trace="${bar_trace} "
fi
# END TRACE
# Initial character position in bar for file:
bar_char=`expr ${bar_pos} / ${bar_want_bps}` || bar_char=0
while test "${bar_char}" -gt `expr ${bar_cur_char} + 4`
do
bar_graph="${bar_graph}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}"
bar_cur_char=`expr ${bar_cur_char} + 5`
done
while test "${bar_char}" -gt "${bar_cur_char}"
do
bar_graph="${bar_graph}${BAR_C1}"
bar_cur_char=`expr ${bar_cur_char} + 1`
done
# Get file size. This must work now (we checked with test -f before).
bar_size1=`eval "${BAR_GET_SIZE}" 2>/dev/null` || bar_size1=0
# BEGIN LARGE
# Divide and upround by pattern matching:
if test -n "${bar_div}"
then
bar_size1=`expr "${bar_size1}" : '\(.*\)'${bar_div}'$'` || bar_size1=0
bar_size1=`expr "${bar_size1}" + 1`
fi
# END LARGE
# loop:
bar_total=0
(
exec 6>&1
exec 5<"${BAR_DIR}${bar_file}${BAR_EXT}"
while test "${bar_total}" -lt "${bar_size1}"
do
dd bs="${bar_bs}" count="${bar_count}${bar_numsuff}" <&5 >&6 2>/dev/null
bar_total=`expr ${bar_total} + ${bar_bps}`
if test "${bar_total}" -gt "${bar_size1}"
then
bar_total="${bar_size1}"
fi
bar_pos1=`expr ${bar_pos} + ${bar_total}`
bar_proz=`expr ${bar_pos1} / ${bar_bph}` || bar_proz=0
# BEGIN PERC
if test "x${BAR_PERC}" = x1
then
bar_perc=" ${bar_proz}% "
bar_perc=`expr "${bar_perc}" : '.*\(.....\)$'`
fi
# END PERC
# BEGIN ETA
if test "x${BAR_ETA}" = x1
then
bar_diff=`eval "${BAR_GET_TIME}" 2>/dev/null` || bar_diff=0
bar_diff=`expr ${bar_diff} - ${bar_t0} 2>/dev/null` || bar_diff=0
bar_100p=`expr 100 - ${bar_proz}` || bar_100p=0
bar_diff=`expr ${bar_diff} '*' ${bar_100p}` || bar_diff=0
bar_diff=`expr ${bar_diff} + ${bar_proz}` || bar_diff=0
bar_diff=`expr ${bar_diff} - 1` || bar_diff=0
bar_diff=`expr ${bar_diff} / ${bar_proz} 2>/dev/null` || bar_diff=0
if test "${bar_diff}" -gt 0
then
bar_t_unit=":"
if test "${bar_diff}" -gt 2700
then
bar_t_uni="h"
bar_diff=`expr ${bar_diff} / 60`
fi
bar_diff_h=`expr ${bar_diff} / 60` || bar_diff_h=0
if test "${bar_diff_h}" -gt 99
then
bar_eta=" ${bar_diff_h}${bar_t_unit} "
else
bar_diff_hi=`expr ${bar_diff_h} '*' 60` || bar_diff_hi=0
bar_diff=`expr ${bar_diff} - ${bar_diff_hi}` || bar_diff=0
bar_diff=`expr "00${bar_diff}" : '.*\(..\)$'`
bar_eta=" ${bar_diff_h}${bar_t_unit}${bar_diff} "
fi
bar_eta=`expr "${bar_eta}" : '.*\(......\)$'`
fi
fi
# END ETA
bar_char=`expr ${bar_pos1} / ${bar_want_bps}` || bar_char=0
while test "${bar_char}" -gt "${bar_cur_char}"
do
bar_graph="${bar_graph}${BAR_C1}"
${bar_trace}${bar_eta}${bar_perc}${bar_graph}${BAR_E_C2}" 1>&2
bar_cur_char=`expr ${bar_cur_char} + 1`
done
done
) | eval "${BAR_CMD}"
bar_pos=`expr ${bar_pos} + ${bar_size1}`
done
# ${BAR_ECHO} "${BAR_E_C1}" "${BAR_R}${BAR_E_C2}" 1>&2
fi
if test "${bar_shown}" = 1
then
# BEGIN TRACE
test "x${BAR_TRACE}" = x1 && bar_trace="${bar_t_space} "
# END TRACE
# BEGIN ETA
test "x${BAR_ETA}" = x1 && bar_eta=' '
# END ETA
if test "x${BAR_CLEAR}" = x1
then
# BEGIN PERC
test "x${BAR_PERC}" = x1 && bar_perc=' '
# END PERC
bar_lauf=0
bar_graph=''
while test `expr ${bar_lauf} + 5` -le "${BAR_WIDTH}"
do
bar_graph="${bar_graph} "
bar_lauf=`expr ${bar_lauf} + 5`
done
while test "${bar_lauf}" -lt "${BAR_WIDTH}"
do
bar_graph="${bar_graph} "
bar_lauf=`expr ${bar_lauf} + 1`
done
${BAR_E_C2}" 1>&2_eta}${bar_perc} ${bar_graph}
else
# BEGIN PERC
test "x${BAR_PERC}" = x1 && bar_perc='100% '
# END PERC
bar_lauf=0
bar_graph=''
while test `expr ${bar_lauf} + 5` -le "${BAR_WIDTH}"
do
bar_graph="${bar_graph}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}"
bar_lauf=`expr ${bar_lauf} + 5`
done
while test "${bar_lauf}" -lt "${BAR_WIDTH}"
do
bar_graph="${bar_graph}${BAR_C1}"
bar_lauf=`expr ${bar_lauf} + 1`
done
${bar_trace}${bar_eta}${bar_perc}${BAR_L}${bar_graph}${BAR_R}${BAR_E_C2}" 1>&2
${BAR_E_NL} 1>&2
fi
fi
}
####>-SCHNAPP-<########################################################
BAR_AWK_0=''
# Command line interface:
while test -n "$1"
do
case "$1" in
-o|-c|-w|-0|-1|-e|-d|-b|-s|-\[\]|-\[|-\]|-T)
if test -z "$2"
then
echo "$0: Error: A non-empty argument was expected after $1" 1>&2
fi
BAR_ARG="$1"
BAR_OPT="$2"
shift
shift
;;
-o*|-c*|-w*|-0*|-1*|-e*|-d*|-b*|-T*)
BAR_ARG=`expr "$1" : '\(-.\)'`
BAR_OPT=`expr "$1" : '-.\(.*\)$'`
shift
;;
-h|-n|-p|-D|-D-|-q|-V|-t|-E|-L)
BAR_ARG="$1"
BAR_OPT=""
shift
;;
--) shift
break
;;
-*) echo "$0: Error: Unrecognized option: $1" 1>&2
exit 1
;;
*)
break
;;
esac
case "${BAR_ARG}" in
-h) echo 'Usage: bar [-n] [-p] [-q] [-o FILE] [-c CMD] [-s SIZE] [-b SIZE]'
echo ' [-w WIDTH] [-0/1/[/] CHAR] [-d DIR] [-e EXT] [Files]'
echo ' bar -V'
echo ' bar -D'
echo ' bar -D-'
echo 'Options:'
echo ' -h displays help'
echo ' -o FILE sets output file'
echo ' -c CMD sets individual execution command'
echo ' -e EXT append an extension to each file'
echo ' -d DIR prepend this prefix to each file (a directory must end in /)'
echo ' -s SIZE expected number of bytes. Use for pipes. This is a hint'
echo ' only that must be greater or equal to the amount actually'
echo ' processed. Further, this only works for single files.'
echo ' -b SIZE maximal block size (bytes) (default: 1048567)'
echo ' -w WIDTH width in characters (default: terminal width-3 or 76)'
echo ' -0 CHAR character for empty bar (default: .)'
echo ' -1 CHAR character for full bar (default: =)'
echo ' -[ CHAR first character of bar (default: [)'
echo ' -] CHAR last character of bar (default: ])'
echo ' -n clears bar after termination'
echo ' -t traces (=displays) which file is processed'
echo ' -T WIDTH no of characters reserved for the file display of -t'
echo ' -p hides percentage'
echo ' -E hides estimated time display'
echo ' -q hides the whole bar, be quiet'
echo ' -D tries to dump the bar_cat() shell function, then exit.'
echo ' Here, -t, -p, -E remove the corresponding feature completely.'
echo ' Further, -L removes large file support from the code.'
echo ' -D- same as -D, but dumps the function body only'
echo ' -V displays version number'
echo ' -- end of options: only file names follow'
exit 0
;;
-n) BAR_CLEAR=1
;;
-L) BAR_LARGE=0
BAR_AWK_0="${BAR_AWK_0} /END *LARGE/ {x=1} ;"
BAR_AWK_0="${BAR_AWK_0} /BEGIN *LARGE/ {x=0} ;"
;;
-t) BAR_TRACE=1
BAR_AWK_0="${BAR_AWK_0} /END *TRACE/ {x=1} ;"
BAR_AWK_0="${BAR_AWK_0} /BEGIN *TRACE/ {x=0} ;"
;;
-T) BAR_TRACE_WIDTH="${BAR_OPT}"
;;
-q) BAR_OK=0
;;
-p) BAR_PERC=0
BAR_AWK_0="${BAR_AWK_0} /END *PERC/ {x=1} ;"
BAR_AWK_0="${BAR_AWK_0} /BEGIN *PERC/ {x=0} ;"
;;
-E) BAR_ETA=0
BAR_AWK_0="${BAR_AWK_0} /END *ETA/ {x=1} ;"
BAR_AWK_0="${BAR_AWK_0} /BEGIN *ETA/ {x=0} ;"
;;
-V) echo "bar v${BAR_VERSION}"
exit 0
;;
-D) echo "BAR_VERSION=${BAR_VERSION}"
awk "${BAR_AWK_0}"'{sub(/ *#.*$/,"")} ; /^bar_cat/ {x=1} ; {sub(/^ */,"")} ; /./ {if(x)print} ; /^}/ {x=0}' "$0"
exit 0
;;
-D-) echo "BAR_VERSION=${BAR_VERSION}"
awk "${BAR_AWK_0}"'{sub(/ *#.*$/,"")} ; /^}/ {x=0} ; {sub(/^ */,"")} ; /./ {if(x)print} ; /^{/ {x=1}' "$0"
exit 0
;;
-o) exec 1>"${BAR_OPT}"
;;
-c) BAR_CMD="${BAR_OPT}"
;;
-b) BAR_BS="${BAR_OPT}"
if BAR_RAW=`expr "${BAR_BS}" : '\(.*\)k$'`
then
BAR_BS=`expr ${BAR_RAW} '*' 1024`
elif BAR_RAW=`expr "${BAR_BS}" : '\(.*\)M$'`
then
BAR_BS=`expr ${BAR_RAW} '*' 1048567`
fi
;;
-s) BAR_SIZE="${BAR_OPT}"
if BAR_RAW=`expr "${BAR_SIZE}" : '\(.*\)k$'`
then
BAR_SIZE=`expr ${BAR_RAW} '*' 1024`
elif BAR_RAW=`expr "${BAR_SIZE}" : '\(.*\)M$'`
then
BAR_SIZE=`expr ${BAR_RAW} '*' 1048567`
fi
if test "$#" -gt 1
then
echo "Error: -s cannot be specified for multiple input files." 1>&2
exit 1
fi
;;
-e) BAR_EXT="${BAR_OPT}"
;;
-d) BAR_DIR="${BAR_OPT}"
;;
-0) BAR_C0="${BAR_OPT}"
;;
-1) BAR_C1="${BAR_OPT}"
;;
-\[) BAR_L="${BAR_OPT}"
;;
-\]) BAR_R="${BAR_OPT}"
;;
-\[\])
BAR_L="${BAR_OPT}"
BAR_R="${BAR_OPT}"
;;
-w) BAR_WIDTH="${BAR_OPT}"
;;
esac
done
# Invoke main function:
if test "$#" = 0
then
bar_cat /dev/stdin
else
bar_cat "$@"
fi