‘bar’ - ‘cat’ with ASCII progress bar

This is a small shell script intended to be used in portable Unix install scripts for showing progress bars.

The overall goal is to write a minimally complex shell script (thus a program that needs no compilation) that is as robust as possible to work on as many Bourne shells and operating systems as possible, and that implements ‘cat’ with an ASCII progress bar and some other nifty features.

This is pure Bourne shell code. (For sh, ash, ksh, zsh, bash, ...)

The script is mainly intended to be used in portable install scripts, where you can use the body of the script.

animated barcat progress bar in ASCII

Source Code


Shell OS Confirmed Script Version(s)
Regularly checked by myself
/bin/sh NetBSD 1.6.2 >= 1.2
/bin/sh Solaris 2.9 >= 1.0
/bin/sh Solaris 2.8 >= 1.2
ae 2.1.8 Linux (Suse) >= 1.2
ash 0.2 Linux (Suse) >= 1.2
bash 1.14.5 Linux (Suse) >= 1.2
bash 2.05.0(1) Linux (Suse) >= 1.0
bash 2.05.0(1) Solaris 2.9 >= 1.2
bash 2.05a.0(1) Linux (Debian) >= 1.2
bash 2.05b.0(1) NetBSD 1.6.2 >= 1.2
bash 3.00.0(1) Solaris 2.8 >= 1.2
jsh 050621 Linux (Suse) >= 1.3
ksh Solaris 2.9 >= 1.2
ksh Solaris 2.8 >= 1.2
pdksh 5.2.14 Linux (Suse) >= 1.2
pdksh 5.2.14 NetBSD 1.6.2 >= 1.2
zsh 4.2.5 Linux (Suse) >= 1.2
zsh 4.0.4 Linux (Debian) >= 1.2
zsh 3.0.8 Solaris 2.9 >= 1.2
Verified by users
ksh AIX 5.0 1.3.1
/bin/sh FreeBSD 5.3 1.3.1
bash Mac OS X 10.4.6 1.4
bash Fedora Core 5 1.4
bash 3.1 Fedora Core 6 1.4
ksh Tru64 Unix 1.4
bash 3.2.13(1) Ubuntu 7.04 Feisty 1.4
bash-3.1.17 Gentoo 2007.0 1.4
bash-3.2.25(1) Ubuntu 7.10 1.4
ksh OpenBSD 4.2 1.4
HP-UX 11i v2 (11.23) 1.4
ksh FreeBSD 7.1-STABLE 1.4
/bin/sh SunOS OpenSolaris i386 5.11 1.4
Bash 4.0 ArchLinux 1.4

Please help improve this project by reporting success or failure in other shells, especially in /bin/sh on other operating systems.


Append files
cat file1 file2 file2 > file3
With Progress Bar:
bar file1 file2 file2 > file3
Copy a file
cp infile outfile
With Progress Bar:
bar -o outfile infile
Copy several files to another directory (showing a common progress bar)
cp file1 file2 file2 outdir/
With Progress Bar:
bar -c 'cat > outdir/${bar_file}' file1 file2 file3
Unpack each file individually, but have a common progress bar
for i in *.tar.gz; do tar xzpf $i; done
With Progress Bar:
bar -c 'tar xzpf -' *.tar.gz

Make a File Listing

We don't want to mention ‘.tar.gz’ in the listing, so we use ‘-e’ to append an extension in each command.
(for file in erwin-2.0.274 bar-1.0-src linux-2.4.21
    echo ${file}:
    tar tzvpf ${file}.tar.gz
done) > package-list.txt
With Progress Bar:
bar -c 'echo ${bar_file}: ; tar tzvpf -' \
    -e .tar.gz                           \
        erwin-2.0.274                    \
        bar-1.0-src                      \
        linux-2.4.21                     \
            > package-list.txt


Options Overview

For the shell script, features are usually passed on the command line, and for the shell function (but also for the script), features are passed as exported environment variables. The following tables gives an overview of the features

Cmd. Line   Env. Var   Description
-o FILE use shell redirection Redirects output to a file.
-n BAR_CLEAR=1 After termination, clear the line with the bar instead of leaving a full bar on the console.
-p BAR_PERC=0 Hides the percentage display.
-E BAR_ETA=0 Hides the estimated time of arrival (ETA) display.
-t BAR_TRACE=1 Shows the current file (trace mode).
-T WIDTH BAR_TRACE_WIDTH=WIDTH Sets the number of characters reserved for the current file display. If not set, the default is 10.
-q BAR_OK=0 Hides the bar completely, and reverts to the plain behaviour of cat.
-c CMD BAR_CMD=CMD For each file, pipe the contents into CMD. The command is evaluated in the shell, you have access to the internal variables of the script. Currently, the most interesting variable is bar_file which contains the name of the current file as given on the command line, i.e., without the additional ending set by BAR_EXT.
-e EXT BAR_EXT=EXT Sets EXT as an additional file extension to be appended to each given file.
-d DIR BAR_DIR=DIR Sets DIR to be prefixed to each given file. Typically used for prepending directories to each file, in which case the argument must end in a slash.
-b SIZE BAR_BS=SIZE Sets the maximal block size to use for dd. By default, this is 1048567 (= 1 MB). The command line option accepts suffixes: k for kilobytes and M for megabytes. The environment variable must be specified in bytes.

Sets the expected number of bytes expected to be processed. Under no circumstances, this may be smaller than the amount of bytes read, as the script might process less bytes of data then (but this is also not good for restricting the data flow to an exact number of bytes, since the internal counting is heuristical: the script will cut the stream at some unpredictable point after the given amount of bytes).

Like -b, the command line accepts k and M suffixes, but only when the result is smaller than the numbers expr can handle.

Without using suffixes, i.e., in bytes, you may specify any amount, even larger ones than expr can handle.

-w WIDTH BAR_WIDTH=WIDTH Sets the bar width to WIDTH characters. The script tries to find a default value by using the environment variable COLUMNS. If it is not set, the default is 76.
-0 CHAR BAR_C0=CHAR Sets the character displayed for work yet to be done.
-1 CHAR BAR_C1=CHAR Sets the character displayed for work already done.
-[ CHAR BAR_A=CHAR Sets the first character displayed in the bar (may be empty).
-] CHAR BAR_B=CHAR Sets the last character displayed in the bar (may be empty).
-V Prints the version number and exits.

Tries to dump the bar_cat() shell function, i.e. everything but the command line interface. The output can be included in your own scripts. This options strips comments, empty lines, and leading white space on all lines to make the result small.

Some features may be removed completely from the dumped shell code by specifying the corresponding options before -D. These are: -t (trace), -p (percentage), -E (ETA) and -L (large file support). (-L only has an effect with -D.)

-D- Same as -D, but only dumps the function body.
-- Last option: only file names follow

Input Files

In the comment line, after the options, the input files follow. The shell function bar_cat is invoked with that list of files.

Note: If the list of files you provide to bar_cat is empty, nothing is read. To explicitly state that stdin is to be read, use the special file name /dev/stdin.

The command line interface invoked with no file names will also read from stdin.



  • The program cannot read from stdin.
    [Fixed in v1.1: stdin]
  • Infinite streams (like /dev/zero) don't work.
    [Fixed in v1.1: inode type]
  • Special devices don't work (the size cannot be determined).
    [Fixed in v1.1: inode type]
  • When neither ‘printf’ nor ‘echo -n’ is found to work, switch off bar display just like for missing ‘dd’ or ‘grep’ -- currently, the normal loop is executed without showing anything. This is actually correct, but quite stupid.
    [Fixed in v1.2: no echo without newline]
  • Files whose size exceeds the computation range of expr cannot be handled properly. On 32-bit machines, this is expected to be the case for file >= 2GB.
    [Fixed in v1.2: use pattern matching to divide]

Missing Features / Future Work






January 17th, 2023
Comments? Suggestions? Corrections? You can drop me a line.