Whether you're a system administrator, developer, or a scripting enthusiast, you've probably found yourself working with Bash at some point. One common challenge faced during script execution is understanding how much of a task has been completed and how much is left to do. This is especially true for tasks that take a significant amount of time to complete. Enter the "Bash progress bar"—a simple yet effective way to visually represent the completion status of a long-running operation.
In the previous article we explored different methods to create progress par using Python programming language. In this comprehensive guide, we will explore everything you need to know about creating and customizing Bash progress bars. From the very basics to advanced techniques, this article aims to provide a one-stop resource for all skill levels. Whether you are a beginner just getting started with Bash or an experienced professional looking to enhance your scripts, there's something here for everyone.
By the end of this article, you'll be well-equipped to implement your own Bash progress bars, improving the user experience and the functionality of your Bash scripts.
Create a simple progress bar
Creating a Bash progress bar can be a simple yet powerful way to enhance the user experience and convey progress for long-running tasks. Below is a basic example of a Bash progress bar script that uses the echo
command.
#!/bin/bash
# Total number of steps
total_steps=20
# Initial progress
progress=0
# Display initial state
echo -n "[--------------------] 0% "
# Simulation of a task
while [ $progress -le $total_steps ]; do
# Calculate the number of '#' to display
let filled_slots=progress*20/total_steps
# Create the progress bar string
bar=""
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
# Create the remaining bar string
for ((i=filled_slots; i<20; i++)); do
bar="${bar}-"
done
# Calculate percentage
let percentage=progress*100/total_steps
# Print the progress bar
echo -ne "\r[${bar}] ${percentage}% "
# Simulate a task by sleeping for 0.1 seconds
sleep 0.1
# Update progress
let progress++
done
echo # Print a newline
Explanation
total_steps=20
: The total number of steps required to complete the task.progress=0
: Indicates the current progress of the task.- The
echo -n "[--------------------] 0%"
line prints the initial state of the Bash progress bar without a trailing newline. - We use a
while
loop to simulate a task. Within this loop, we do the following:
Calculate Filled Slots: The number of '#' characters required is calculated based on the progress.
let filled_slots=progress*20/total_steps
Create Progress Bar String: Empty strings ("-"
) and filled strings ("#"
), are concatenated to create the progress bar.
bar=""
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
Print the Bash Progress Bar: We use echo -ne
to print the progress bar, where:
-n
ensures that the cursor doesn't move to the next line.-e
enables the interpretation of backslash escapes, like\r
(Carriage Return).
echo -ne "\r[${bar}] ${percentage}% "
Sleep: sleep 0.1
is used to simulate some task running for a brief period.
Create Multi-step Progress Bar
Creating a multi-step Bash progress bar can add more granularity to your scripts by allowing you to track the completion of various subtasks within a larger operation. Let's consider a scenario where you have a script that performs three major tasks. Each task takes a different amount of time to complete, and you want to display a separate progress bar for each task.
#!/bin/bash
# Function to display a progress bar
display_progress_bar() {
local total_steps=$1
local progress=0
local bar=""
echo -n "[--------------------] 0% "
while [ $progress -le $total_steps ]; do
let filled_slots=progress*20/total_steps
bar=""
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
for ((i=filled_slots; i<20; i++)); do
bar="${bar}-"
done
let percentage=progress*100/total_steps
echo -ne "\r[${bar}] ${percentage}% "
sleep 0.1
let progress++
done
echo
}
# Task 1
echo "Starting Task 1..."
display_progress_bar 20
# Task 2
echo "Starting Task 2..."
display_progress_bar 30
# Task 3
echo "Starting Task 3..."
display_progress_bar 25
echo "All tasks completed!"
Explanation
This function encapsulates the logic for displaying a single progress bar. It takes one argument, total_steps
, which indicates the total number of steps for the particular task.
Each task is represented by a separate call to the display_progress_bar
function, with a varying number of total steps.
Task 1: Takes 20 steps to complete.
echo "Starting Task 1..."
display_progress_bar 20
Task 2: Takes 30 steps to complete.
echo "Starting Task 2..."
display_progress_bar 30
Task 3: Takes 25 steps to complete.
echo "Starting Task 3..."
display_progress_bar 25
Create Progress Bar with Percentage Display
Displaying the percentage of completion alongside the graphical representation can make a shell script progress bar more informative and easier to interpret. Here's an example that shows how to create a progress bar in a shell script that also displays the percentage of the task completed.
#!/bin/bash
# Function to display progress bar with percentage
display_progress_bar_with_percentage() {
local total_steps=$1
local progress=0
local bar=""
local percentage=0
echo -n "[--------------------] 0% "
while [ $progress -le $total_steps ]; do
let filled_slots=progress*20/total_steps
bar=""
# Generate the filled portion of the bar
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
# Generate the unfilled portion of the bar
for ((i=filled_slots; i<20; i++)); do
bar="${bar}-"
done
# Calculate the percentage of completion
let percentage=progress*100/total_steps
# Display the bar and percentage
echo -ne "\r[${bar}] ${percentage}% "
# Simulate some work
sleep 0.1
# Increment the progress
let progress++
done
echo # Move to the next line after completion
}
# Main execution
echo "Starting the operation..."
display_progress_bar_with_percentage 100
echo "Operation completed!"
Explanation
Variables
total_steps
: The total number of steps that the operation will take.progress
: Keeps track of the current progress.bar
: Stores the visual representation of the shell script progress bar.percentage
: Stores the calculated percentage of completion.
While Loop
Within the while
loop, the script performs the following operations:
Calculate Filled Slots: Determines how many #
characters are needed in the progress bar.
let filled_slots=progress*20/total_steps
Generate Progress Bar: Filled (#
) and unfilled (-
) portions of the progress bar are created.
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
Calculate Percentage: Determines the percentage of the task that has been completed.
let percentage=progress*100/total_steps
Display Progress Bar and Percentage: Both the graphical progress bar and the percentage of completion are displayed using the echo
command.
echo -ne "\r[${bar}] ${percentage}% "
Simulate Work: sleep 0.1
is used to simulate the operation we are tracking.
Create Bash Progress Bar with Dynamic Sizing
Dynamic sizing in a Bash progress bar allows you to adjust the length of the progress bar according to a specified parameter or even the terminal window size. This enhances the user experience by providing a more flexible and visually pleasing display.
#!/bin/bash
# Function to display a dynamically sized progress bar
display_dynamic_progress_bar() {
local total_steps=$1
local progress=0
local terminal_width=$(tput cols)
# Deduct 10 to leave space for percentage display and brackets
let bar_width=terminal_width-10
while [ $progress -le $total_steps ]; do
let filled_slots=progress*bar_width/total_steps
bar=""
# Generate the filled portion of the bar
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
# Generate the unfilled portion of the bar
for ((i=filled_slots; i<bar_width; i++)); do
bar="${bar}-"
done
# Calculate the percentage of completion
let percentage=progress*100/total_steps
# Display the bar and percentage
echo -ne "\r[${bar}] ${percentage}% "
# Simulate some work
sleep 0.1
# Increment the progress
let progress++
done
echo # Move to the next line after completion
}
# Main execution
echo "Starting the operation..."
display_dynamic_progress_bar 100
echo "Operation completed!"
Explanation
Fetching Terminal Width
The script first fetches the terminal's width using the command tput cols
, and stores it in the variable terminal_width
.
local terminal_width=$(tput cols)
Calculating Dynamic Bar Width
We then subtract 10 from terminal_width
to accommodate the brackets [ ]
and percentage display.
let bar_width=terminal_width-10
Dynamic Bar Sizing in While Loop
Within the loop, the script dynamically adjusts the number of filled (#
) and unfilled (-
) slots based on the calculated bar_width
.
let filled_slots=progress*bar_width/total_steps
The rest of the script is similar to the previous examples, but now it utilizes this dynamically calculated bar_width
.
Customizing Progress Bar Styles
Creating visually appealing and informative progress indicators is crucial for enhancing user experience. Various styles of progress bars can be used, each serving a different purpose and aesthetic. In this section, we'll look at different types of Bash progress bars and shell script progress bars, including horizontal and vertical progress bars, colored progress bars, and text-based indicators like spinners.
1. Horizontal Bash Progress Bar
The horizontal progress bar is the most traditional and commonly used style.
#!/bin/bash
total_steps=20
for ((progress=0; progress<=total_steps; progress++)); do
let filled_slots=progress*20/total_steps
bar=""
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
echo -ne "\r[${bar}]"
sleep 0.1
done
2. Vertical Shell Script Progress Bar
Vertical progress bars occupy less horizontal space and can be useful in specific scenarios.
#!/bin/bash
total_steps=20
for ((progress=0; progress<=total_steps; progress++)); do
echo -ne "\e[20A" # Move 20 lines up
for ((i=0; i<progress; i++)); do
echo "#"
done
for ((i=progress; i<total_steps; i++)); do
echo "-"
done
sleep 0.1
echo -e "\e[20B" # Move 20 lines down
done
3. Colored Bash Progress Bar
Adding color can make your bash progress bar more visually appealing. You can use ANSI escape codes for coloring.
#!/bin/bash
echo -ne "\033[31m" # Set color to red
total_steps=20
for ((progress=0; progress<=total_steps; progress++)); do
let filled_slots=progress*20/total_steps
bar=""
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}#"
done
echo -ne "\r[${bar}]"
sleep 0.1
done
echo -ne "\033[0m" # Reset color
4. Text-Based Progress Indicators (Spinner)
Spinners are another form of shell script progress bar that are useful when the total steps are unknown.
#!/bin/bash
spinner="/|\\-/"
echo -n "Processing "
while :; do
for i in {0..3}; do
echo -ne "\b${spinner:i:1}"
sleep 0.1
done
done
5. Adding Additional Information (e.g., Time Remaining)
Adding time estimates to your progress bar can make it more informative. Here's a Bash progress bar that also estimates the time remaining.
#!/bin/bash
start_time=$(date +%s)
total_steps=100
for ((i=1; i<=total_steps; i++)); do
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))
estimated_time=$((elapsed_time * total_steps / i - elapsed_time))
echo -ne "\rProgress: [$i/$total_steps] - Estimated Time Remaining: ${estimated_time}s"
sleep 0.1
done
echo # Newline
6. Customizing Appearance
You can customize the appearance of the shell script progress bar by using different characters or adding colors.
#!/bin/bash
total_steps=20
echo -ne "\033[34m" # Set color to blue
for ((progress=0; progress<=total_steps; progress++)); do
let filled_slots=progress*40/total_steps
bar=""
for ((i=0; i<$filled_slots; i++)); do
bar="${bar}="
done
echo -ne "\r[${bar}]"
sleep 0.1
done
echo -ne "\033[0m" # Reset color
7. Keyboard Interruptions
It's useful to allow the user to interrupt the bash progress bar cleanly. Here's how you can capture keyboard interruptions like Ctrl+C in your script.
#!/bin/bash
trap "echo; echo 'Operation aborted.'; exit 1" SIGINT SIGTERM
total_steps=100
for ((i=1; i<=total_steps; i++)); do
sleep 0.1
echo -ne "\rProgress: [$i/$total_steps]"
done
echo # Newline
In this script, pressing Ctrl+C will trigger the message "Operation aborted" and the script will exit.
Using External Libraries to create Progress Bar
Using external libraries or commands can provide a quick and feature-rich way to implement progress bars in your Bash scripts. Here, we'll look at some popular options like pv
, dialog
, and third-party Bash libraries to add bash progress bars or indicators.
1. Using pv
command
The pv
(Pipe Viewer) command allows you to view the progress of data through a pipeline. It's extremely handy for tracking the status of commands that process data.
First you will need to install pv
as it is not installed by default in most distributions:
# Install pv on Debian/Ubuntu
sudo apt update
sudo apt install pv
# Install pv on CentOS/RHEL/Fedora
sudo yum install pv
# Or for newer Fedora versions
# sudo dnf install pv
# Install pv on macOS
brew install pv
Here is a simple example to use pv while performing a copy operation to display bash progress bar.
# Copying a file with progress
cat large_file.txt | pv -s $(stat -c %s large_file.txt) > output.txt
In this example, pv
shows a text-based bash progress bar indicating how much data has been processed.
You can use pv
to monitor the transfer rate and progress when compressing a file with gzip
.
pv large_file.txt | gzip > large_file.txt.gz
Here's how you can see a progress bar when searching for a particular string in a large text file.
pv large_file.txt | grep "search_string"
When exporting a large MySQL database, you can track the progress as follows:
mysqldump -u username -p database_name | pv -i 0.5 > database_dump.sql
2. Using dialog
command
To install diag utility, you can use the different package managers such as yum, apt or brew based on your distribution:
# Install dialog on Debian/Ubuntu
sudo apt update
sudo apt install dialog
# Install dialog on CentOS/RHEL/Fedora
sudo yum install dialog
# Or for newer Fedora versions
# sudo dnf install dialog
# Install dialog on macOS
brew install dialog
The dialog
command allows you to create a variety of user-interface widgets in a shell script, including a GUI-like progress box.
#!/bin/bash
(
for i in {0..100}; do
echo $i
sleep 0.1
done
) | dialog --title "File Progress" --gauge "Please wait" 10 70 0
Here, the dialog
command displays a graphical progress box that updates as the loop iterates through numbers from 0 to 100.
You can chain multiple progress bars together for multi-step operations.
#!/bin/bash
{
for i in {0..25}; do
echo $i
sleep 0.1
done
echo "XXX"
echo "Performing Step 1..."
echo "XXX"
for i in {26..50}; do
echo $i
sleep 0.1
done
echo "XXX"
echo "Performing Step 2..."
echo "XXX"
for i in {51..100}; do
echo $i
sleep 0.1
done
} | dialog --title "Multi-Step Operation" --gauge "Initializing..." 10 70 0
Conclusion
Progress bars are powerful tools for enhancing the user experience in shell scripting. They offer a visual indicator of an ongoing operation, making it easier for users to understand the status and estimated completion time. Throughout this article, we have explored different ways to implement a Bash progress bar, covering use cases from file transfer and script execution to data processing and web scraping. We've also dived into some tips and tricks to elevate the functionality of these progress indicators.
- Basic Progress Bars: For beginners, the simple one-liner
pv
commands or basic loop structures offer a quick introduction. - Advanced Progress Bars: For those with more experience, multi-step progress bars, external libraries, and custom styles and appearances bring a higher level of sophistication.
- Tips and Tricks: Adding time estimates, customizing appearances, and handling keyboard interruptions make your progress bars more robust and user-friendly.
Additional Resources
- Official Documentation
- Stack Overflow Threads