]>
Tony Duckles's Git Repositories (git.nynim.org) - dotfiles.git/blob - bin/bar
4 # 'cat' with ASCII progress bar
9 # 'bar' works just like 'cat', but shows a progress bar in ASCII art on stderr.
10 # The script's main function is meant to be usable in any Bourne shell to be
11 # suitable for install scripts without the need for any additional tool.
13 # Shell Script Usage: bar [options] [files]
21 # : bar mypack.tar.bz2 | tar xjpf -
23 # Individual pipe for each file:
25 # : bar -c 'tar xjpf -' mypack1.tar.bz2 mypack2.tar.bz2
27 # Individual pipe, using ${bar_file} variable:
29 # : bar -c 'echo ${bar_file}: ; gzip -dc | tar tvf -' \
31 # : file1 file2 file3 file4 file5 \
32 # : > package-list.txt
34 #####################################################
35 # Programs and shell commands:
37 # Required (otherwise this fails):
39 # if, then, else, fi, expr, test, cat, eval, exec
58 # Optional (otherwise this does not show the bar):
60 # grep, dd, echo, ls, sed, cut
63 # must output the file size at fifth position.
65 # The command line interface also uses:
70 ####>-SCHNIPP-<########################################################
73 # Use this shell function in your own install scripts.
75 #####################################################
78 # Width of the bar (in ten characters). The default is 76 characters.
79 test -z "${BAR_WIDTH}" && test -n "${COLUMNS}" && BAR_WIDTH
=${COLUMNS}
82 ( expr "${BAR_WIDTH}" + 0 >/dev
/null
2>&1 ) || BAR_WIDTH
=0
83 BAR_WIDTH
=`expr ${BAR_WIDTH} + 0` || BAR_WIDTH
=0
84 test "x${BAR_WIDTH}" = x0
&& BAR_WIDTH
=76
86 # Maximal block size to use for dd.
87 test -n "${BAR_BS}" || BAR_BS
=1048567
90 # Whether to show a percentage.
91 test -n "${BAR_PERC}" || BAR_PERC
=1
95 # Whether to show estimated time of arrival (ETA).
96 test -n "${BAR_ETA}" || BAR_ETA
=1
99 # Width of the trace display:
101 test -n "${BAR_TRACE_WIDTH}" || BAR_TRACE_WIDTH
=10
104 # The command to execute for every given file. Each file
105 # is piped into this command individually. By default, the
106 # files are simply dumped to stdout.
107 test -n "${BAR_CMD}" || BAR_CMD
=cat
109 # The characters to be used in the bar
110 test -n "${BAR_L}" || BAR_L
='['
111 test -n "${BAR_R}" || BAR_R
=']'
112 test -n "${BAR_C0}" || BAR_C0
='.'
113 test -n "${BAR_C1}" || BAR_C1
='='
115 # Additional extension to add to each file:
118 # Whether to clear bar after termination. Otherwise keep the full bar.
119 #BAR_CLEAR=${BAR_CLEAR-0}
121 # Unless switched off by user, use the bar by default:
122 test -n "${BAR_OK}" || BAR_OK
=1
124 #####################################################
125 BAR_WIDTH
=`expr ${BAR_WIDTH} - 3`
129 if test "x${BAR_TRACE}" = x1
131 BAR_WIDTH
=`expr ${BAR_WIDTH} - ${BAR_TRACE_WIDTH}`
132 bar_lauf
=${BAR_TRACE_WIDTH}
135 while test "${bar_lauf}" -gt 1
137 bar_t_space
="${bar_t_space} "
138 bar_t_dot
="${bar_t_dot}."
139 bar_lauf
=`expr ${bar_lauf} - 1`
141 bar_trace
="${bar_t_space} "
148 ( expr 1 + ${SECONDS} >/dev
/null
2>&1 ) || BAR_ETA
=0
149 if test "x${BAR_ETA}" = x1
151 BAR_GET_TIME
='( echo ${SECONDS} )'
152 BAR_WIDTH
=`expr ${BAR_WIDTH} - 6`
159 if test "x${BAR_PERC}" = x1
161 BAR_WIDTH
=`expr ${BAR_WIDTH} - 5`
166 BAR_GET_SIZE
='( ls -l "${BAR_DIR}${bar_file}${BAR_EXT}" | sed "s@ *@ @g" | cut -d " " -f 5 ) 2>/dev/null'
170 ( ( echo a
) >/dev
/null
2>&1 ) || BAR_OK
=0
171 ( ( echo a
| dd bs
=2 count
=2 ) >/dev
/null
2>&1 ) || BAR_OK
=0
172 ( ( echo a
| grep a
) >/dev
/null
2>&1 ) || BAR_OK
=0
173 ( ( echo a
| sed 's@ *@ @g' ) >/dev
/null
2>&1 ) || BAR_OK
=0
174 ( ( echo a
| cut
-d ' ' -f 1 ) >/dev
/null
2>&1 ) || BAR_OK
=0
177 test "${BAR_WIDTH}" -ge 4 || BAR_OK
=0
184 # Does echo accept -n without signalling an error?
185 if echo -n abc
>/dev
/null
2>&1
190 # Check how to print a line without newline:
191 if ( ( ${BAR_ECHO} "${BAR_E_C1}" abc
; echo 1,2,3 ) | grep n
) >/dev
/null
2>&1
194 if ( ( ${BAR_ECHO} 'xyz\c' ; echo 1,2,3 ) | grep c
) >/dev
/null
2>&1
197 if ( ( printf 'ab%s' c
; echo 1,2,3 ) | grep abc
) >/dev
/null
2>&1
213 # prepare initial bar:
215 if test "${BAR_OK}" = 1
219 while test `expr ${bar_lauf} + 5` -le "${BAR_WIDTH}"
221 bar_graph
="${bar_graph}${BAR_C0}${BAR_C0}${BAR_C0}${BAR_C0}${BAR_C0}"
222 bar_lauf
=`expr ${bar_lauf} + 5`
224 while test "${bar_lauf}" -lt "${BAR_WIDTH}"
226 bar_graph
="${bar_graph}${BAR_C0}"
227 bar_lauf
=`expr ${bar_lauf} + 1`
229 ${BAR_ECHO} "${BAR_E_C1}" "
${bar_trace}${bar_eta}${bar_perc}${BAR_L}${bar_graph}${BAR_R}
${BAR_E_C2}" 1>&2
233 # for shifting large numbers so that expr can handle them:
234 # Assume we can compute up to 2147483647, thus 9 arbitrary digits.
235 # We must be able to do + of two numbers of 9 digits length. Ok.
237 ( ( test 1999999998 = `expr 999999999 + 999999999` ) >/dev
/null
2>&1 ) || BAR_OK
=0
238 bar_large_num
="........."
245 if test -n "${BAR_SIZE}"
249 while expr "${bar_size}" : "${bar_large_num}" >/dev
/null
2>&1
251 bar_div
="${bar_div}."
252 bar_numsuff
="${bar_numsuff}0"
253 bar_size
=`expr "${bar_size}" : '\(.*\).$'`
256 BAR_GET_SIZE
="echo '${BAR_SIZE}'"
261 if test -f "${BAR_DIR}${bar_file}${BAR_EXT}"
263 bar_size1
=`eval "${BAR_GET_SIZE}"`
266 # divide and upround by pattern matching:
267 if test -n "${bar_div}"
269 bar_size1
=`expr "${bar_size1}" : '\(.*\)'${bar_div}'$'` || bar_size1
=0
272 # adjust if still too large:
273 while expr "${bar_size1}" : "${bar_large_num}" >/dev
/null
2>&1
275 bar_div
="${bar_div}."
276 bar_numsuff
="${bar_numsuff}0"
277 bar_size1
=`expr "${bar_size1}" : '\(.*\).$'`
278 bar_size
=`expr "${bar_size}" : '\(.*\).$'` || bar_size
=0
281 # upround if necessary:
282 if test -n "${bar_div}"
284 bar_size1
=`expr "${bar_size1}" + 1`
289 bar_size
=`expr ${bar_size} + ${bar_size1}`
292 # adjust if still too large:
293 while expr "${bar_size}" : "${bar_large_num}" >/dev
/null
2>&1
295 bar_div
="${bar_div}."
296 bar_numsuff
="${bar_numsuff}0"
297 bar_size
=`expr "${bar_size}" : '\(.*\).$'`
306 bar_quad
=`expr ${BAR_WIDTH} '*' ${BAR_WIDTH}`
307 test "${bar_size}" -gt "${bar_quad}" || BAR_OK
=0
309 if test "${BAR_OK}" = 0
311 # For some reason, we cannot display the bar. Thus plain operation:
314 if test "${bar_file}" = "/dev/stdin"
318 eval "${BAR_CMD}" < "${BAR_DIR}${bar_file}${BAR_EXT}"
322 # Compute wanted bytes per step:
323 bar_want_bps
=`expr ${bar_size} + ${BAR_WIDTH}`
324 bar_want_bps
=`expr ${bar_want_bps} - 1`
325 bar_want_bps
=`expr ${bar_want_bps} / ${BAR_WIDTH}`
327 # Compute block count per step to keep within maximum block size:
329 if test "${bar_want_bps}" -gt "${BAR_BS}"
331 bar_count
=`expr ${bar_want_bps} + ${BAR_BS}`
332 bar_count
=`expr ${bar_count} - 1`
333 bar_count
=`expr ${bar_count} / ${BAR_BS}`
336 # Compute block size for given count:
337 bar_wc
=`expr ${BAR_WIDTH} '*' ${bar_count}`
339 bar_bs
=`expr ${bar_size} + ${bar_wc}`
340 bar_bs
=`expr ${bar_bs} - 1`
341 bar_bs
=`expr ${bar_bs} / ${bar_wc}`
343 # Compute bs * count, the bytes per step:
344 bar_bps
=`expr ${bar_bs} '*' ${bar_count}`
346 # Compute bytes per hundredth:
347 bar_bph
=`expr ${bar_size} + 99`
348 bar_bph
=`expr ${bar_bph} / 100`
355 bar_t0
=`eval "${BAR_GET_TIME}" 2>/dev/null` || bar_t0
=0
359 if test "x${BAR_TRACE}" = x1
361 bar_trace
=`expr "${bar_file}" : '.*/\([^/][^/]*\)$'` || bar_trace
="${bar_file}"
362 bar_trace
=`expr "${bar_trace}${bar_t_space}" : '\('${bar_t_dot}'\)'`
363 bar_trace
="${bar_trace} "
366 # Initial character position in bar for file:
367 bar_char
=`expr ${bar_pos} / ${bar_want_bps}` || bar_char
=0
368 while test "${bar_char}" -gt `expr ${bar_cur_char} + 4`
370 bar_graph
="${bar_graph}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}"
371 bar_cur_char
=`expr ${bar_cur_char} + 5`
373 while test "${bar_char}" -gt "${bar_cur_char}"
375 bar_graph
="${bar_graph}${BAR_C1}"
376 bar_cur_char
=`expr ${bar_cur_char} + 1`
379 # Get file size. This must work now (we checked with test -f before).
380 bar_size1
=`eval "${BAR_GET_SIZE}" 2>/dev/null` || bar_size1
=0
383 # Divide and upround by pattern matching:
384 if test -n "${bar_div}"
386 bar_size1
=`expr "${bar_size1}" : '\(.*\)'${bar_div}'$'` || bar_size1
=0
387 bar_size1
=`expr "${bar_size1}" + 1`
395 exec 5<"${BAR_DIR}${bar_file}${BAR_EXT}"
396 while test "${bar_total}" -lt "${bar_size1}"
398 dd bs
="${bar_bs}" count
="${bar_count}${bar_numsuff}" <&5 >&6 2>/dev
/null
399 bar_total
=`expr ${bar_total} + ${bar_bps}`
400 if test "${bar_total}" -gt "${bar_size1}"
402 bar_total
="${bar_size1}"
404 bar_pos1
=`expr ${bar_pos} + ${bar_total}`
405 bar_proz
=`expr ${bar_pos1} / ${bar_bph}` || bar_proz
=0
407 if test "x${BAR_PERC}" = x1
409 bar_perc
=" ${bar_proz}% "
410 bar_perc
=`expr "${bar_perc}" : '.*\(.....\)$'`
414 if test "x${BAR_ETA}" = x1
416 bar_diff
=`eval "${BAR_GET_TIME}" 2>/dev/null` || bar_diff
=0
417 bar_diff
=`expr ${bar_diff} - ${bar_t0} 2>/dev/null` || bar_diff
=0
418 bar_100p
=`expr 100 - ${bar_proz}` || bar_100p
=0
419 bar_diff
=`expr ${bar_diff} '*' ${bar_100p}` || bar_diff
=0
420 bar_diff
=`expr ${bar_diff} + ${bar_proz}` || bar_diff
=0
421 bar_diff
=`expr ${bar_diff} - 1` || bar_diff
=0
422 bar_diff
=`expr ${bar_diff} / ${bar_proz} 2>/dev/null` || bar_diff
=0
423 if test "${bar_diff}" -gt 0
426 if test "${bar_diff}" -gt 2700
429 bar_diff
=`expr ${bar_diff} / 60`
431 bar_diff_h
=`expr ${bar_diff} / 60` || bar_diff_h
=0
432 if test "${bar_diff_h}" -gt 99
434 bar_eta
=" ${bar_diff_h}${bar_t_unit} "
436 bar_diff_hi
=`expr ${bar_diff_h} '*' 60` || bar_diff_hi
=0
437 bar_diff
=`expr ${bar_diff} - ${bar_diff_hi}` || bar_diff
=0
438 bar_diff
=`expr "00${bar_diff}" : '.*\(..\)$'`
439 bar_eta
=" ${bar_diff_h}${bar_t_unit}${bar_diff} "
441 bar_eta
=`expr "${bar_eta}" : '.*\(......\)$'`
446 bar_char
=`expr ${bar_pos1} / ${bar_want_bps}` || bar_char
=0
447 while test "${bar_char}" -gt "${bar_cur_char}"
449 bar_graph
="${bar_graph}${BAR_C1}"
450 ${BAR_ECHO} "${BAR_E_C1}" "
${bar_trace}${bar_eta}${bar_perc}${bar_graph}${BAR_E_C2}" 1>&2
451 bar_cur_char
=`expr ${bar_cur_char} + 1`
454 ) | eval "${BAR_CMD}"
455 bar_pos
=`expr ${bar_pos} + ${bar_size1}`
457 # ${BAR_ECHO} "${BAR_E_C1}" "${BAR_R}${BAR_E_C2}" 1>&2
460 if test "${bar_shown}" = 1
463 test "x${BAR_TRACE}" = x1
&& bar_trace
="${bar_t_space} "
466 test "x${BAR_ETA}" = x1
&& bar_eta
=' '
468 if test "x${BAR_CLEAR}" = x1
471 test "x${BAR_PERC}" = x1
&& bar_perc
=' '
475 while test `expr ${bar_lauf} + 5` -le "${BAR_WIDTH}"
477 bar_graph
="${bar_graph} "
478 bar_lauf
=`expr ${bar_lauf} + 5`
480 while test "${bar_lauf}" -lt "${BAR_WIDTH}"
482 bar_graph
="${bar_graph} "
483 bar_lauf
=`expr ${bar_lauf} + 1`
485 ${BAR_ECHO} "${BAR_E_C1}" "
${bar_trace}${bar_eta}${bar_perc} ${bar_graph}
${BAR_E_C2}" 1>&2
488 test "x${BAR_PERC}" = x1
&& bar_perc
='100% '
492 while test `expr ${bar_lauf} + 5` -le "${BAR_WIDTH}"
494 bar_graph
="${bar_graph}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}${BAR_C1}"
495 bar_lauf
=`expr ${bar_lauf} + 5`
497 while test "${bar_lauf}" -lt "${BAR_WIDTH}"
499 bar_graph
="${bar_graph}${BAR_C1}"
500 bar_lauf
=`expr ${bar_lauf} + 1`
502 ${BAR_ECHO} "${BAR_E_C1}" "
${bar_trace}${bar_eta}${bar_perc}${BAR_L}${bar_graph}${BAR_R}${BAR_E_C2}" 1>&2
507 ####>-SCHNAPP-<########################################################
511 # Command line interface:
515 -o|-c|-w|-0|-1|-e|-d|-b|-s|-\
[\
]|-\
[|-\
]|-T)
518 echo "$0: Error: A non-empty argument was expected after $1" 1>&2
525 -o*|-c*|-w*|-0*|-1*|-e*|-d*|-b*|-T*)
526 BAR_ARG
=`expr "$1" : '\(-.\)'`
527 BAR_OPT
=`expr "$1" : '-.\(.*\)$'`
530 -h|-n|-p|-D|-D-|-q|-V|-t|-E|-L)
538 -*) echo "$0: Error: Unrecognized option: $1" 1>&2
547 -h) echo 'Usage: bar [-n] [-p] [-q] [-o FILE] [-c CMD] [-s SIZE] [-b SIZE]'
548 echo ' [-w WIDTH] [-0/1/[/] CHAR] [-d DIR] [-e EXT] [Files]'
553 echo ' -h displays help'
554 echo ' -o FILE sets output file'
555 echo ' -c CMD sets individual execution command'
556 echo ' -e EXT append an extension to each file'
557 echo ' -d DIR prepend this prefix to each file (a directory must end in /)'
558 echo ' -s SIZE expected number of bytes. Use for pipes. This is a hint'
559 echo ' only that must be greater or equal to the amount actually'
560 echo ' processed. Further, this only works for single files.'
561 echo ' -b SIZE maximal block size (bytes) (default: 1048567)'
562 echo ' -w WIDTH width in characters (default: terminal width-3 or 76)'
563 echo ' -0 CHAR character for empty bar (default: .)'
564 echo ' -1 CHAR character for full bar (default: =)'
565 echo ' -[ CHAR first character of bar (default: [)'
566 echo ' -] CHAR last character of bar (default: ])'
567 echo ' -n clears bar after termination'
568 echo ' -t traces (=displays) which file is processed'
569 echo ' -T WIDTH no of characters reserved for the file display of -t'
570 echo ' -p hides percentage'
571 echo ' -E hides estimated time display'
572 echo ' -q hides the whole bar, be quiet'
573 echo ' -D tries to dump the bar_cat() shell function, then exit.'
574 echo ' Here, -t, -p, -E remove the corresponding feature completely.'
575 echo ' Further, -L removes large file support from the code.'
576 echo ' -D- same as -D, but dumps the function body only'
577 echo ' -V displays version number'
578 echo ' -- end of options: only file names follow'
584 BAR_AWK_0
="${BAR_AWK_0} /END *LARGE/ {x=1} ;"
585 BAR_AWK_0
="${BAR_AWK_0} /BEGIN *LARGE/ {x=0} ;"
588 BAR_AWK_0
="${BAR_AWK_0} /END *TRACE/ {x=1} ;"
589 BAR_AWK_0
="${BAR_AWK_0} /BEGIN *TRACE/ {x=0} ;"
591 -T) BAR_TRACE_WIDTH
="${BAR_OPT}"
596 BAR_AWK_0
="${BAR_AWK_0} /END *PERC/ {x=1} ;"
597 BAR_AWK_0
="${BAR_AWK_0} /BEGIN *PERC/ {x=0} ;"
600 BAR_AWK_0
="${BAR_AWK_0} /END *ETA/ {x=1} ;"
601 BAR_AWK_0
="${BAR_AWK_0} /BEGIN *ETA/ {x=0} ;"
603 -V) echo "bar v${BAR_VERSION}"
606 -D) echo "BAR_VERSION=${BAR_VERSION}"
607 awk "${BAR_AWK_0}"'{sub(/ *#.*$/,"")} ; /^bar_cat/ {x=1} ; {sub(/^ */,"")} ; /./ {if(x)print} ; /^}/ {x=0}' "$0"
610 -D-) echo "BAR_VERSION=${BAR_VERSION}"
611 awk "${BAR_AWK_0}"'{sub(/ *#.*$/,"")} ; /^}/ {x=0} ; {sub(/^ */,"")} ; /./ {if(x)print} ; /^{/ {x=1}' "$0"
614 -o) exec 1>"${BAR_OPT}"
616 -c) BAR_CMD
="${BAR_OPT}"
618 -b) BAR_BS
="${BAR_OPT}"
619 if BAR_RAW
=`expr "${BAR_BS}" : '\(.*\)k$'`
621 BAR_BS
=`expr ${BAR_RAW} '*' 1024`
622 elif BAR_RAW
=`expr "${BAR_BS}" : '\(.*\)M$'`
624 BAR_BS
=`expr ${BAR_RAW} '*' 1048567`
627 -s) BAR_SIZE
="${BAR_OPT}"
628 if BAR_RAW
=`expr "${BAR_SIZE}" : '\(.*\)k$'`
630 BAR_SIZE
=`expr ${BAR_RAW} '*' 1024`
631 elif BAR_RAW
=`expr "${BAR_SIZE}" : '\(.*\)M$'`
633 BAR_SIZE
=`expr ${BAR_RAW} '*' 1048567`
637 echo "Error: -s cannot be specified for multiple input files." 1>&2
641 -e) BAR_EXT
="${BAR_OPT}"
643 -d) BAR_DIR
="${BAR_OPT}"
645 -0) BAR_C0
="${BAR_OPT}"
647 -1) BAR_C1
="${BAR_OPT}"
649 -\
[) BAR_L
="${BAR_OPT}"
651 -\
]) BAR_R
="${BAR_OPT}"
657 -w) BAR_WIDTH
="${BAR_OPT}"
662 # Invoke main function: