A terminal progress bar is useful only when your script knows how much work is complete. Use tqdm for a Python loop with a known total, or use Bash printf with a carriage return when the script owns the counter.
Use a progress bar only when you can measure work
A percentage needs a numerator and a total. File counts, byte totals, and a fixed list of jobs work well. A spinner is the honest alternative when a command may take time but cannot report completion.
Terminal output is easier to follow when you already know how Linux command-line navigation and file commands work. If the job runs from a script, shell scripting basics and cron jobs gives the surrounding execution model.
Show Python loop progress with tqdm
tqdm wraps an iterable and writes updates to the terminal while your loop advances. It can calculate a percentage, item count, rate, and estimated remaining time because range(5) has a length.
python -m pip install tqdm
Save the following file as python_progress.py. The loop sleeps only to make the changing display visible. Replace sleep with the work that processes each item.
from time import sleep
from tqdm import trange
for _ in trange(5, desc='Copying files', unit='file', ncols=56):
sleep(0.03)
Run the script with the Python environment where you installed tqdm.
./venv/bin/python python_progress.py

The final 5/5 line shows that every loop iteration finished. tqdm redraws a single terminal line through carriage-return control characters, so its changing states may appear as separate lines in a captured terminal image.
Build a Bash progress bar with printf
Bash has no built-in percentage widget, but it can redraw one line. This function derives filled and empty widths from the counter, then printf returns the cursor to the start of that line before the next update.
#!/usr/bin/env bash
set -euo pipefail
for current in $(seq 0 10); do
width=20
filled=$((current * width / 10))
empty=$((width - filled))
printf -v filled_bar '%*s' "$filled" ''
printf -v empty_bar '%*s' "$empty" ''
filled_bar=${filled_bar// /#}
empty_bar=${empty_bar// /-}
printf '\r[%s%s] %3d%%' "$filled_bar" "$empty_bar" "$((current * 10))"
sleep 0.03
done
printf '\n'
Save it as bash_progress.sh and run the exact command below.
bash bash_progress.sh

At 0%, the bar contains only dashes. At 100%, all twenty positions are filled. Keep printf rather than echo here because printf gives the script exact control over the carriage return and percentage formatting.
Choose the display that matches the task
Use a determinate bar when you know the total. Use a spinner when waiting for a service, download, or subprocess that exposes no count. A bar that guesses completion is worse than a spinner because it claims information the program does not have.
- Wrap a sized Python iterable with tqdm when each iteration represents one unit of work.
- Track current and total in Bash when your script controls the loop.
- Use a spinner when the program cannot measure a total.
- Send progress output to the terminal, not a data stream that another command must parse.
Existing Python progress displays
The retained examples below show distinct Python terminal displays. Keep the display simple when the task only needs a count and percentage.

This tqdm example shows a determinate meter. Use it when the iterable itself supplies the total.
Animated Python bars and spinners
alive-progress adds an animated meter with throughput and estimated time, which earns its place only when rate feedback changes how you monitor a long-running loop.

The animation is visual feedback, not a substitute for a correct total.
This second alive-progress state shows the meter later in the same kind of workload.

Keep it only when the changing rate or remaining-time display helps you monitor a long operation.
Halo shows a spinner for work without a known total.

Use a spinner instead of inventing a percentage for an opaque subprocess.
Yaspin is another spinner option for a task that has started but cannot expose a total.

A spinner should stop with a success or failure message so the terminal does not leave an ambiguous active state.
Existing Bash display examples
The retained Bash captures show a text bar, an animated terminal state, and gauge-style interfaces. A plain text bar remains the best fit for a portable script that only needs Bash and a terminal.

This captured bar shows the same filled-versus-empty idea used in the current Bash example.
The next state demonstrates that a progress display can animate while work continues.

Animation is optional. Preserve readable counts or percentages when a log will be reviewed later.
A gauge interface can help when a script is intentionally interactive.

Before choosing an interactive gauge, check which shell is running your script and whether the target system has its required utility.
The final gauge example keeps that interface distinction visible.

For an unattended job, send concise status to a log and reserve terminal redraws for an interactive session.
Avoid broken progress output
A progress bar should end with a newline. Without it, the next shell prompt lands on the same line as the completed bar. If you distribute the Bash script, use chmod and ownership guidance before making it executable.
Terminal configuration can also change colors, prompts, and startup behavior. Bash profile and bashrc settings explain where those settings belong. Keep the progress calculation in the script rather than in either startup file.
Frequently asked questions
Start with tqdm when a Python iterable already has a length. In Bash, keep the counter and total next to the work loop so the displayed percentage remains tied to work the script can measure.
