Tuesday, 22 October 2024

How to Add a Progress Bar to a Shell Script

When writing shell scripts in Bash (or other *NIX shells), adding a progress bar can improve the user experience, especially when executing long-running tasks like file transfers, backups, or compressions. This post explores several techniques to implement progress bars, with different examples from the ones you’ve seen before.

1. Simple Progress Bar Using printf

A simple and effective method is using printf and \r to update the terminal line. Here’s how you can create a basic progress bar that shows the completion percentage as the task progresses:

#!/bin/bash

total=50  # Total units to represent

for ((i=1; i<=total; i++)); do
    sleep 0.1  # Simulate some work being done
    progress=$((i * 100 / total))
    printf "\rProgress: [%-50s] %d%%" $(head -c $((i)) < /dev/zero | tr '\0' '#') $progress
done
echo -e "\nTask complete!"

Explanation:

  • The head -c and tr combination fills the progress bar with # symbols as the loop progresses.
  • \r returns the cursor to the beginning of the line, allowing you to overwrite the previous output without creating new lines.
  • The sleep simulates work being done.

2. Progress Bar for Copy Operations with rsync

If you’re performing copy or sync operations, tools like rsync already have a built-in progress option. You can use the --progress flag to track the status of file transfers:

rsync --progress /source/directory /destination/directory

The --progress flag gives detailed output, including how much data has been copied, how much is left, and the estimated time for completion.

3. Advanced Progress Bar with a Function

To reuse the progress bar logic in multiple parts of your script, you can encapsulate it into a function. This function calculates the percentage and displays the progress accordingly.

#!/bin/bash

# Function to display a progress bar
progress_bar() {
    local current=$1
    local total=$2
    local width=50

    local progress=$((current * width / total))
    local remaining=$((width - progress))

    printf "\r[%-${width}s] %d%%" $(head -c $progress < /dev/zero | tr '\0' '#') $((current * 100 / total))
}

# Simulate a task with a progress bar
total_steps=100
for step in $(seq 1 $total_steps); do
    sleep 0.1
    progress_bar $step $total_steps
done

echo -e "\nCompleted!"

Explanation:

  • progress_bar takes the current step and the total steps as input to calculate the percentage.
  • The printf and string manipulation fill the bar dynamically as the task progresses.

4. Spinner for Long Tasks

Instead of a progress bar, sometimes a spinner can give users visual feedback for long-running operations. Here’s a simple spinner implementation:

#!/bin/bash

spinner() {
    local sp='/-\|'
    local delay=0.1
    while :; do
        for i in {0..3}; do
            printf "\r%s" "${sp:i:1}"
            sleep $delay
        done
    done
}

# Example usage
echo "Processing..."
spinner &  # Run spinner in the background
SPINNER_PID=$!

# Simulate a long task
sleep 10

# Stop the spinner
kill $SPINNER_PID
echo -e "\nDone!"

Explanation:

  • The spinner rotates characters (/, -, \, |) to simulate progress.
  • spinner & runs the spinner in the background, allowing the main task to continue.
  • After the task completes, kill stops the spinner.

5. Using pv for Piped Progress Tracking

pv is a tool that visualizes the progress of data being piped through it. It’s particularly useful for tracking the progress of file transfers or compression operations.

Example usage for copying a file:

pv source_file > destination_file

For a more practical example, let’s say you’re compressing a large directory:

tar cf - /path/to/directory | pv | gzip > archive.tar.gz

Here, pv shows the progress of the compression, including the speed and estimated time remaining.

6. Progress Bar with Percentage in a Loop

You can combine both a progress bar and a percentage indicator in a loop. This method gives users both visual and numerical feedback.

#!/bin/bash

# Variables
total=20
current=0

# Display progress
while [ $current -le $total ]; do
    # Calculate progress percentage
    percent=$((current * 100 / total))
    
    # Draw progress bar
    bar=$(printf "%-${total}s" "#" | head -c $current)
    
    # Print progress bar and percentage
    printf "\r[%-20s] %d%%" "$bar" "$percent"
    
    # Simulate work
    sleep 0.5
    current=$((current + 1))
done

echo -e "\nProcess complete!"

Explanation:

  • printf prints the progress bar (#) and updates the percentage.
  • The sleep simulates task progression over time.

Adding a progress bar or spinner to your shell scripts enhances user interaction, especially when tasks take a long time. Whether using printf for custom progress bars, the built-in --progress in rsync, or external tools like pv, these approaches can be tailored to your specific needs. Now you can provide real-time feedback to users while your shell scripts perform lengthy operations.

Labels:

0 Comments:

Post a Comment

Note: only a member of this blog may post a comment.

<< Home