Differences between revisions 2 and 12 (spanning 10 versions)
Revision 2 as of 2007-06-18 19:59:25
Size: 1015
Editor: GreyCat
Comment:
Revision 12 as of 2010-06-25 20:12:14
Size: 4215
Editor: MatthiasPopp
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
[[Anchor(faq44)]]
== How do I create a progress bar? ==

The easiest way is to use {{{dialog --gauge}}}. Here is an example, which relies heavily on BASH features:
<<Anchor(faq44)>>
== How do I create a progress bar?  How do I see a progress indicator when copying/moving files? ==
The easiest way to add a progress bar to your own script is to use {{{dialog --gauge}}}. Here is an example, which relies heavily on [[BASH]] features:
Line 7: Line 6:
   # We want to process all of the *.zip files in the current directory.
   files=(*.zip)
   dialog --gauge "Working..." 20 75 < <(
    n=${#files[*]}; i=0
    for f in "${files[@]}"; do
  # process "$f" in some way (for testing, "sleep 1")
  echo $((100*(++i)/n))
      done)
# Bash
# P
rocess all of the *.zip files in the current directory.
files=(*.zip)
dialog --gauge "Working..." 20 75 < <(
   n=${#files[*]}; i=0
   for f in "${files[@]}"; do
      # process "$f" in some way (for testing, "sleep 1")
      echo $((100*(++i)/n))
   done
)
Line 18: Line 19:
 * An array named {{{files}}} is populated with all the files we want to process.
 * {{{dialog}}} is invoked, and its input is redirected from a process substitution. (A pipe could also be used here; we'd simply have to reverse the {{{dialog}}} command and the loop.)
 * An [[BashFAQ/005|array]] named {{{files}}} is populated with all the files we want to process.
 * {{{dialog}}} is invoked, and its input is redirected from a ProcessSubstitution. (A pipe could also be used here; we'd simply have to reverse the {{{dialog}}} command and the loop.)
Line 23: Line 24:
For more examples of using {{{dialog}}}, see [#faq40 FAQ #40]. For more examples of using {{{dialog}}}, see [[BashFAQ/040|FAQ #40]].

A simple progress bar can also be programmed without `dialog`. There are lots of different approaches, depending on what kind of presentation you're looking for.

One traditional approach is the [[BashFAQ/034|spinner]] which shows a whirling line segment to indicate "busy". This is not really a "progress meter" since there is no information presented about how close the program is to completion.

The next step up is presenting a numeric value without scrolling the screen. Using a carriage return to move the cursor to the beginning of the line (on a graphical terminal, not a teletype...), and not writing a newline until the very end:
{{{
i=0
while ((i < 100)); do
  printf "\r%3d%% complete" $i
  ((i += RANDOM%5+2))
  # Of course, in real life, we'd be getting i from somewhere meaningful.
  sleep 1
done
echo
}}}
Of note here is the `%3d` in the `printf` format specifier. It's important to use a fixed-width field for displaying the numbers, especially if the numbers may count downward (first displaying 10 and then 9). Of course we're counting upwards here, but that may not always be the case in general. If a fixed-width field is not desired, then printing a bunch of spaces at the end may help remove any clutter from previous lines.

If an actual "bar" is desired, rather than a number, then one may be drawn using ASCII characters:
{{{
bar="=================================================="
barlength=${#bar}
i=0
while ((i < 100)); do
  # Number of bar segments to draw.
  n=$((i*barlength / 100))
  printf "\r[%-${barlength}s]" "${bar:0:n}"
  ((i += RANDOM%5+2))
  # Of course, in real life, we'd be getting i from somewhere meaningful.
  sleep 1
done
echo
}}}
Naturally one may choose a bar of a different length, or composed of a different set of characters, e.g., you can have a colored progress bar
{{{
files=(*)
width=${COLUMNS-$(tput cols)}
rev=$(tput rev)

n=${#files[*]}
i=0
printf "$(tput setab 0)%${width}s\r"
for f in "${files[@]}"; do
   # process "$f" in some way (for testing, "sleep 1")
   printf "$rev%$((width*++i/n))s\r" " "
done
tput sgr0
echo
}}}

=== When copying/moving files ===
You can't get a progress indicator with `cp(1)`, but you can either:

 * build one yourself with tools such as [[http://www.ivarch.com/programs/pv.shtml|pv]] or [[http://clpbar.sourceforge.net/|clpbar]];
 * use some other tool, e.g. [[http://members.iinet.net.au/~lynx/vcp/|vcp]].

You may want to use pv(1) since it's packaged for many systems. In that case, it's convenient if you create a function or script to wrap it.

For example:

{{{
pv "$1" > "$2/${1##*/}"
}}}

This lacks error checking and support for moving files.

you can also use [[http://samba.anu.edu.au/rsync/|rsync]]:

{{{
rsync -avx --progress --stats "$1" "$2"
}}}

Please note that the "total" of files can change each time rsync enters a directory and finds more/less files that it expected, but at least is more info than cp. Rsync progress is good for big transfers with small files.

----
CategoryShell

How do I create a progress bar? How do I see a progress indicator when copying/moving files?

The easiest way to add a progress bar to your own script is to use dialog --gauge. Here is an example, which relies heavily on BASH features:

# Bash
# Process all of the *.zip files in the current directory.
files=(*.zip)
dialog --gauge "Working..." 20 75 < <(
   n=${#files[*]}; i=0
   for f in "${files[@]}"; do
      # process "$f" in some way (for testing, "sleep 1")
      echo $((100*(++i)/n))
   done
)

Here's an explanation of what it's doing:

  • An array named files is populated with all the files we want to process.

  • dialog is invoked, and its input is redirected from a ProcessSubstitution. (A pipe could also be used here; we'd simply have to reverse the dialog command and the loop.)

  • The processing loop iterates over the array.
  • Every time a file is processed, it increments a counter (i), and writes the percent complete to stdout.

For more examples of using dialog, see FAQ #40.

A simple progress bar can also be programmed without dialog. There are lots of different approaches, depending on what kind of presentation you're looking for.

One traditional approach is the spinner which shows a whirling line segment to indicate "busy". This is not really a "progress meter" since there is no information presented about how close the program is to completion.

The next step up is presenting a numeric value without scrolling the screen. Using a carriage return to move the cursor to the beginning of the line (on a graphical terminal, not a teletype...), and not writing a newline until the very end:

i=0
while ((i < 100)); do
  printf "\r%3d%% complete" $i
  ((i += RANDOM%5+2))
  # Of course, in real life, we'd be getting i from somewhere meaningful.
  sleep 1
done
echo

Of note here is the %3d in the printf format specifier. It's important to use a fixed-width field for displaying the numbers, especially if the numbers may count downward (first displaying 10 and then 9). Of course we're counting upwards here, but that may not always be the case in general. If a fixed-width field is not desired, then printing a bunch of spaces at the end may help remove any clutter from previous lines.

If an actual "bar" is desired, rather than a number, then one may be drawn using ASCII characters:

bar="=================================================="
barlength=${#bar}
i=0
while ((i < 100)); do
  # Number of bar segments to draw.
  n=$((i*barlength / 100))
  printf "\r[%-${barlength}s]" "${bar:0:n}"
  ((i += RANDOM%5+2))
  # Of course, in real life, we'd be getting i from somewhere meaningful.
  sleep 1
done
echo

Naturally one may choose a bar of a different length, or composed of a different set of characters, e.g., you can have a colored progress bar

files=(*)
width=${COLUMNS-$(tput cols)}
rev=$(tput rev)

n=${#files[*]}
i=0
printf "$(tput setab 0)%${width}s\r"
for f in "${files[@]}"; do
   # process "$f" in some way (for testing, "sleep 1")
   printf "$rev%$((width*++i/n))s\r" " "
done
tput sgr0
echo

When copying/moving files

You can't get a progress indicator with cp(1), but you can either:

  • build one yourself with tools such as pv or clpbar;

  • use some other tool, e.g. vcp.

You may want to use pv(1) since it's packaged for many systems. In that case, it's convenient if you create a function or script to wrap it.

For example:

pv "$1" > "$2/${1##*/}"

This lacks error checking and support for moving files.

you can also use rsync:

rsync -avx --progress --stats "$1" "$2"

Please note that the "total" of files can change each time rsync enters a directory and finds more/less files that it expected, but at least is more info than cp. Rsync progress is good for big transfers with small files.


CategoryShell

BashFAQ/044 (last edited 2017-11-13 22:19:11 by GreyCat)