How can I create a progress bar with bash?
This is my script:
#!/bin/bash pass='number1 number12 number13 number14 number15 number16' chk='number14' for i in $pass ; do if [ "$i" == "$chk" ]; then echo ' Found ^_^' else echo 'loading 50%' fi doneI want to replace echo 'loading 50%' with anything to create a progress bar.
5 Answers
whiptail comes preinstalled on Ubuntu and many other distros, and will show full-screen (but still terminal-based) progress elements.
dialog is a superset of whiptail, so this example will work equally well with both. It does provide more advanced UI elements, so it may come in handy if you're looking for user interaction such as file pickers and forms, but it has the disadvantage of not coming preinstalled on many systems.
for i in $(seq 1 100)
do sleep 0.1 echo $i
done | whiptail --title 'Test script' --gauge 'Running...' 6 60 0Note that the script output is interpreted as a percentage, so you may have to adjust your output accordingly.
Whiptail and Dialog also allow you to modify the text at run time via a rather cryptic syntax:
phases=( 'Locating Jebediah Kerman...' 'Motivating Kerbals...' 'Treating Kessler Syndrome...' 'Recruiting Kerbals...'
)
for i in $(seq 1 100); do sleep 0.1 if [ $i -eq 100 ]; then echo -e "XXX\n100\nDone!\nXXX" elif [ $(($i % 25)) -eq 0 ]; then let "phase = $i / 25" echo -e "XXX\n$i\n${phases[phase]}\nXXX" else echo $i fi
done | whiptail --title 'Kerbal Space Program' --gauge "${phases[0]}" 6 60 0pv shows the progress of a file or stream being piped through it. It cannot however be (easily?) used to show progress of a custom operation such as a loop. It's designed specifically for streams.
$ head -c 1G < /dev/urandom | pv -s 1G > /dev/null 277MB 0:00:16 [17.4MB/s] [========> ] 27% ETA 0:00:43Some real-world examples where pv comes in handy:
# progress while importing a DB dump
pv mybigfile.sql | mysql -uroot -p dbname
# importing straight from a remote server
ssh user@server 'cat mybigfile.sql.gz' | pv | gzip -cd | mysql -uroot -p dbname
# taking a snapshot of a btrfs partition
btrfs send /snapshots/$date | pv | btrfs receive /mnt/backup/rootI don't know of any commands that give one-line progress bars in the style of pv or wget, but there are plenty of simple Bash/Perl/sed scripts that will add that functionality, as others have shared here.
You can use zenity to create simple GTK dialog windows. One of the available options is a progress bar dialog.
You create such a window using zenity --progress. To make it useful, you should specify more information by adding some of the options below (excerpt from man zenity):
Progress options --text=STRING Set the dialog text --percentage=INT Set initial percentage --auto-close Close dialog when 100% has been reached --auto-kill Kill parent process if cancel button is pressed --pulsate Pulsate progress bar --no-cancel Hides the cancel buttonThere are two modes:
pulsating: The progress bar is pulsating, it just indicates that something is running, but does not tell anything about the progress. You do this by setting the
--pulsatingoption.manual: You have to pipe the current progress percentage to the
zenitycommand's standard input to update the progress bar.
An example for this could look like that below. Note that the previous commands are grouped to a subshell so that all the output is redirected to thezenitydialog and not just that of the last command:(echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress
This code will do it, and doesn't require anything (other than bash, of course). It prints # signs, like you asked in your comment:
pass='number1 number12 number13 number14 number15 number16'
chk='number14'
passarr=($pass)
lenProgressBar=${#passarr[@]}
echo -n '['
i=0
while [ $i -lt $lenProgressBar ]; do echo -n '-' ((i++))
done
echo -n ']'
i=0
while [ $i -lt $lenProgressBar ]; do echo -e -n '\b' ((i++))
done
echo -e -n '\b'
for i in $pass ; do if [ "$i" = "$chk" ]; then echo -e '#\nFound ^_^' break else echo -n '#' fi
doneHowever, if you have a lot to check, this will just fill your screen with # signs. To fix that issue, try this code:
lenProgressBar=5
pass='number1 number12 number13 number14 number15 number16'
chk='number14'
passarr=($pass)
lenPass=${#passarr[@]}
if [ $lenProgressBar -gt $lenPass ]; then lenProgressBar=lenPass
elif [ $lenProgressBar -lt 1 ]; then lenProgressBar=1
fi
let "chksForEqualsPrint = $lenPass / $lenProgressBar"
echo -n '['
i=0
while [ $i -lt $lenProgressBar ]; do echo -n '-' ((i++))
done
echo -n ']'
i=0
while [ $i -lt $lenProgressBar ]; do echo -e -n '\b' ((i++))
done
echo -e -n '\b'
n=1
for i in $pass ; do if [ "$i" = "$chk" ]; then echo -e '\nFound ^_^' break else if [ $n -eq $chksForEqualsPrint ]; then echo -n '#' n=1 else ((n++)) fi fi
doneChange the 5 in the first line (lenProgressBar=5) to the length that you want your progress bar to be. It'll take longer to print a # sign with lower length progress bars than with higher length ones, but don't let the length of the progress bar exceed your screen size; it won't work well if you do. (It won't let you use a progress bar higher than the number of items you're checking or lower than 1)
Here's another approach using ansi escape codes:
#!/bin/bash
pass='number1 number2 number 3 number4 number12 number13 number14 number15 number16'
chk='number15'
result="Not Found!"
echo
echo -n "Working... "
echo -ne "\033[1;32m\033[7m\033[?25l"
for i in $pass ; do sleep .4s if [ "$i" == "$chk" ]; then result=" Found ^_^" break else echo -n " " fi
done
echo -ne "\r\033[0m\033[K\033[?25h"
echo $result
echo From Stack Overflow
I needed a progress bar that would fit in popup bubble message (notify-send) to represent TV volume level. Recently I've been writing a music player in python and the TV picture is turned off most of the time.
Sample output from terminal
Bash script
#!/bin/bash
# Show a progress bar at step number $1 (from 0 to 100)
function is_int() { test "$@" -eq "$@" 2> /dev/null; }
# Parameter 1 must be integer
if ! is_int "$1" ; then echo "Not an integer: ${1}" exit 1
fi
# Parameter 1 must be >= 0 and <= 100
if [ "$1" -ge 0 ] && [ "$1" -le 100 ] 2>/dev/null
then :
else echo bad volume: ${1} exit 1
fi
# Main function designed for quickly copying to another program
Main () { Bar="" # Progress Bar / Volume level Len=25 # Length of Progress Bar / Volume level Div=4 # Divisor into Volume for # of blocks Fill="▒" # Fill up to $Len Arr=( "▉" "▎" "▌" "▊" ) # UTF-8 left blocks: 7/8, 1/4, 1/2, 3/4 FullBlock=$((${1} / Div)) # Number of full blocks PartBlock=$((${1} % Div)) # Size of partial block (array index) while [[ $FullBlock -gt 0 ]]; do Bar="$Bar${Arr[0]}" # Add 1 full block into Progress Bar (( FullBlock-- )) # Decrement full blocks counter done # If remainder zero no partial block, else append character from array if [[ $PartBlock -gt 0 ]]; then Bar="$Bar${Arr[$PartBlock]}" fi while [[ "${#Bar}" -lt "$Len" ]]; do Bar="$Bar$Fill" # Pad Progress Bar with fill character done echo Volume: "$1 $Bar" exit 0 # Remove this line when copying into program
} # Main
Main "$@"Test bash script
Use this script to test the progress bar in the terminal.
#!/bin/bash
# test_progress_bar3
Main () { tput civis # Turn off cursor for ((i=0; i<=100; i++)); do CurrLevel=$(./progress_bar3 "$i") # Generate progress bar 0 to 100 echo -ne "$CurrLevel"\\r # Reprint overtop same line sleep .04 done echo -e \\n # Advance line to keep last progress echo "$0 Done" tput cnorm # Turn cursor back on
} # Main
Main "$@"TL;DR
This section details how notify-send is used to quickly spam popup bubble messages to the desktop. This is required because volume level can change many times a second and the default bubble message behavior is for a message to stay on the desktop for many seconds.
Sample popup bubble message
Popup bubble message bash code
From the script above the main function was copied to a new functioned called VolumeBar in an existing bash script called tvpowered. The exit 0 command in the copied main function was removed.
Here's how to call it and let Ubuntu's notify-send command know we will be spamming popup bubble message:
VolumeBar $CurrVolume
# Ask Ubuntu:
notify-send --urgency=critical "tvpowered" \ -h string:x-canonical-private-synchronous:volume \ --icon=/usr/share/icons/gnome/48x48/devices/audio-speakers.png \ "Volume: $CurrVolume $Bar"This is the new line which tells notify-send to immediately replace last popup bubble:
-h string:x-canonical-private-synchronous:volume \volume groups the popup bubble messages together and new messages in this group immediately replaces the previous. You can use anything instead of volume.