When copying large files in Bash shell scripts, it would be nice to have a progress bar displayed. Unfortunately the cp command does not have a progress bar option.
The following script shows how we can implement a copy function in Bash that displays a progress bar with ETA (Estimated Time to Arrival). It simulates the cp command by piping the stdin and stdout of cat, dd and cat in series. The dd command is encapsulated in a while loop so that it can perform the copying in chunks of 1MB and also to print the progress bar. Note that the progress bar is printed on stderr as stdin and stdout are used to transfer the bytes.
copy() { # src dst [width]
srcsize=$(stat -c %s $1) || return $?
dstsize=0
width=${3:-25}
mega=$(( 1024 * 1024 ))
start=$(date +%s)
cat $1 | (
while [[ dstsize -lt srcsize ]]
do
dd bs=512 count=2048 2>/dev/null || return $?
(( dstsize += $mega ))
[[ dstsize -gt srcsize ]] && dstsize=$srcsize
# print truncated filename
name=$(basename $1 | cut -b -20)
printf "\r%-20s " $name 1>&2
# print percentage
percent=$(( 100 * $dstsize / $srcsize ))
printf "%3d%% [" $percent 1>&2
# print progress bar
bar=$(( $width * $dstsize / $srcsize ))
for i in $(seq 1 $bar); do printf "=" 1>&2; done
for i in $(seq $bar $(( $width-1 ))); do printf " " 1>&2; done
# print size of file copied
if [[ $dstsize -le 1024 ]]; then
printf -v size "%d" $dstsize;
elif [[ $dstsize -le $mega ]]; then
printf -v size "%d kB" $(( $dstsize / 1024 ));
else
printf -v size "%d MB" $(( $dstsize / $mega ));
fi
printf "] %7s" "$size" 1>&2
# print estimated time of arrival
elapsed=$(( $(date +%s) - $start ))
remain=$(( $srcsize - $dstsize ))
eta=$(( ($elapsed * $remain) / $dstsize + 1))
if [[ $remain == 0 ]]; then eta=0; fi
etamin=$(( $eta / 60 ))
etasec=$(( $eta % 60 ))
if [[ $eta > 0 ]]; then etastr="ETA"; else etastr=" "; fi
printf " %02d:%02d $etastr" $etamin $etasec 1>&2
done
echo 1>&2
) | cat >$2
}
The inspiration for this code came from studying the progress bar implemented at http://www.theiling.de/projects/bar.html. The major differences of the above code from bar are in the progress bar format and the use of the extra cat command at the beginning. The above code is also less generic but that’s fine with me since it is simpler and shorter.
To use the above code, simply cut and paste it into the top of your shell script. Then wherever you have cp srcfile dstfile replace it with copy srcfile dstfile to see the progress bar. Note that the copy function is not a drop-in replacement for cp since it handles neither options nor wildcards.
