Bash Progress Bars
Basic Bash loop progress bar
for i in {1..100}; do printf "\r[%3d%%] %-${i}s" "$i" "#" | tr ' ' '#'; sleep 0.05; done; echo
For loop – simple percentage bar
for i in {1..100}; do
printf "\rProgress: [%-50s] %3d%%" "$(printf '#%.0s' $(seq 1 $((i/2))))" "$i"
sleep 0.05
done
echo
Use a for loop – simple percentage bar
#!/usr/bin/env bash
for i in {1..100}; do
printf "\rProgress: [%-50s] %3d%%" "$(printf '#%.0s' $(seq 1 $((i/2))))" "$i"
sleep 0.05
done
echo
Use a while loop – counter based
#!/usr/bin/env bash
i=0
while [ $i -le 100 ]; do
filled=$((i/2))
printf "\r[%.*s%*s] %3d%%" "$filled" "##################################################" "$((50-filled))" "" "$i"
sleep 0.05
((i++))
done
echo
Use a until loop – run until complete
#!/usr/bin/env bash
i=0
until [ $i -gt 100 ]; do
printf "\rProgress: %3d%%" "$i"
sleep 0.05
((i++))
done
echo
Spinner (no percentage, task-based)
- (Stop with
Ctrl+Cor kill from parent process.)
#!/usr/bin/env bash
spinner='|/-\'
i=0
while sleep 0.1; do
printf "\r%c Working..." "${spinner:i++%4:1}"
done
Progress bar based on number of tasks
#!/usr/bin/env bash
tasks=(a b c d e f g h i j)
total=${#tasks[@]}
count=0
for task in "${tasks[@]}"; do
((count++))
percent=$((count*100/total))
printf "\r[%3d%%] %s" "$percent" "$task"
sleep 0.3
done
echo
Use seq + for – compact style
#!/usr/bin/env bash
for i in $(seq 0 100); do
printf "\rProgress: %3d%%" "$i"
sleep 0.03
done
echo
File-based progress (bytes copied)
#!/usr/bin/env bash
src="bigfile"
size=$(stat -c%s "$src")
while true; do
copied=$(stat -c%s "$src.copy" 2>/dev/null || echo 0)
percent=$((copied*100/size))
printf "\rCopying: %3d%%" "$percent"
[ "$percent" -ge 100 ] && break
sleep 0.2
done
echo
Use a read loop (pipe-driven progress)
#!/usr/bin/env bash
total=100
count=0
yes | head -n $total | while read; do
((count++))
printf "\rProgress: %3d%%" $((count*100/total))
sleep 0.05
done
echo
Use trap-safe progress bar function
progress() {
for i in {1..100}; do
printf "\r[%3d%%]" "$i"
sleep 0.05
done
}
trap 'echo; exit' INT
progress
echo
Unicode bar (modern terminals)
#!/usr/bin/env bash
blocks=("▏" "▎" "▍" "▌" "▋" "▊" "▉" "█")
for i in {1..80}; do
printf "\rProgress: %s" "${blocks[i%8]}"
sleep 0.05
done
echo
Use pv (Pipe Viewer) percentage bar
dd if=/dev/zero bs=1M count=100 2>/dev/null | pv -pt | dd of=/dev/null
Use pv with explicit size
pv -s 100M </dev/zero >/dev/null
Using yes + pv
yes | pv -l -s 1000 | head -n 1000 >/dev/null
Use dialog gauge (text UI progress bar)
for i in {1..100}; do echo $i; sleep 0.05; done | dialog --gauge "Working..." 7 50
Use whiptail gauge
for i in {1..100}; do echo $i; sleep 0.05; done | whiptail --gauge "Processing..." 6 50 0
Pure awk progress bar
awk 'BEGIN{for(i=1;i<=100;i++){printf "\r[%3d%%] %s",i,substr("##################################################",1,i/2); system("sleep 0.05")}}'
Use rsync built-in progress bar
rsync -a --info=progress2 /usr/bin/ /tmp/bin-copy/
Use dd with status progress
dd if=/dev/zero of=/dev/null bs=1M count=200 status=progress
ETA-aware progress bar (dynamic time remaining)
#!/usr/bin/env bash
total=100
start=$(date +%s)
for i in $(seq 1 $total); do
now=$(date +%s)
elapsed=$((now - start))
eta=$((elapsed * (total - i) / i))
printf "\r[%3d%%] Elapsed: %2ds | ETA: %2ds" "$i" "$elapsed" "$eta"
sleep 0.05
done
echo
Adaptive-width progress bar (auto-fits terminal size)
#!/usr/bin/env bash
for i in {1..100}; do
cols=$(tput cols)
bar_width=$((cols - 10))
filled=$((i * bar_width / 100))
printf "\r[%s%s] %3d%%" \
"$(printf '%*s' "$filled" | tr ' ' '#')" \
"$(printf '%*s' "$((bar_width-filled))")" \
"$i"
sleep 0.05
done
echo
Color-gradient progress bar (true terminal candy)
#!/usr/bin/env bash
for i in {0..100}; do
color=$((31 + i % 6))
printf "\r\033[%sm█\033[0m %3d%%" "$color" "$i"
sleep 0.04
done
echo
Signal-driven progress bar (external control)
#!/usr/bin/env bash
progress=0
trap '((progress+=10))' USR1
trap 'echo; exit' INT
while true; do
printf "\rProgress: %3d%%" "$progress"
[ "$progress" -ge 100 ] && break
sleep 0.1
done
echo
Multi-stage pipeline progress bar (step-based)
#!/usr/bin/env bash
steps=("Download" "Verify" "Extract" "Compile" "Install")
total=${#steps[@]}
for i in "${!steps[@]}"; do
percent=$(((i+1)*100/total))
printf "\r[%3d%%] %s..." "$percent" "${steps[i]}"
sleep 0.7
done
echo
Bash progress indicator with time estimation
count=0
total=34
pstr="[=======================================================================]"
while [ $count -lt $total ]; do
sleep 0.5 # this is work
count=$(( $count + 1 ))
pd=$(( $count * 73 / $total ))
printf "\r%3d.%1d%% %.${pd}s" $(( $count * 100 / $total )) $(( ($count * 1000 / $total) % 10 )) $pstr
done
Bash progress bar with elapsed time and remaining estimate
count=0
total=34
start=`date +%s`
while [ $count -lt $total ]; do
sleep 0.5 # this is work
cur=`date +%s`
count=$(( $count + 1 ))
pd=$(( $count * 73 / $total ))
runtime=$(( $cur-$start ))
estremain=$(( ($runtime * $total / $count)-$runtime ))
printf "\r%d.%d%% complete ($count of $total) - est %d:%0.2d remaining\e[K" $(( $count*100/$total )) $(( ($count*1000/$total)%10)) $(( $estremain/60 )) $(( $estremain%60 ))
done
printf "\ndone\n"
High-resolution Braille progress bar (8× smoother than blocks)
chars=(⣀ ⣄ ⣆ ⣇ ⣧ ⣷ ⣿)
total=100
for ((i=0;i<=total;i++)); do
idx=$(( i * ${#chars[@]} / total ))
printf "\rProgress: %s %3d%%" "${chars[idx]}" "$i"
sleep 0.04
done
echo
Drop-in loop (use with any set above)
| Chars | Description |
|---|---|
| chars=(⣀ ⣄ ⣆ ⣇ ⣧ ⣷ ⣿) | Braille-based left-to-right fill; very smooth perceived progress |
| chars=(⠁ ⠃ ⠇ ⠏ ⠟ ⠿ ⣿) | Braille dot density ramp; excellent for subtle progress indication |
| chars=(░ ▒ ▓ █) | Classic ASCII-style shading; highly compatible and readable |
| chars=(▏ ▎ ▍ ▌ ▋ ▊ ▉ █) | Fine-grained vertical block fill; precise and fluid |
| chars=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █) | Horizontal bar growth; ideal for traditional progress meters |
| chars=(▱ ▰ █) | Sparse diagonal-style blocks; minimal but expressive |
| chars=(○ ◔ ◑ ◕ ●) | Circular fill progression; visually intuitive completion cue |
| chars=(▢ ▣ ■) | Square outline-to-solid fill; clean and structured look |
| chars=(· : ∙ • ●) | Dot intensity ramp; subtle, lightweight progress feedback |
| chars=(╱ ╲ ╳ █) | Cross-stroke animation; dynamic, spinner-like feel |
| chars=(⠂ ⠄ ⠆ ⠇ ⠧ ⠷ ⠿) | Minimal braille ramp; smooth motion with low visual noise |
| chars=(⣁ ⣃ ⣇ ⣏ ⣟ ⣯ ⣷ ⣿) | Heavy braille gradient; dense and highly visible fill |
| chars=(▖ ▘ ▌ ▛ █) | Quadrant block buildup (left-biased); crisp stepwise growth |
| chars=(▗ ▙ ▐ ▜ █) | Quadrant block buildup (right-biased); mirrored visual flow |
| chars=(▘ ▝ ▖ ▗ ▙ ▛ ▜ ▟ █) | Full quadrant animation; very smooth and visually rich |
| chars=(╌ ╍ ╎ ╏ █) | Line thickness ramp; looks like increasing stroke weight |
| chars=(┈ ┉ ┊ ┋ █) | Dashed vertical density increase; rhythmic and readable |
| chars=(◌ ◔ ◑ ◕ ●) | Alternate circle fill; softer and more decorative |
| chars=(⬒ ⬔ ⬕ ⬖ ⬗ ⬘ ⬙ ⬛) | Geometric square morph; modern, UI-like appearance |
| chars=(▭ ▮ █) | Minimal block fill; clean and fast visual feedback |
chars=(╱ ╲ ╳ █) # swap with any set above
total=100
for ((i=0;i<=total;i++)); do
idx=$(( i * (${#chars[@]} - 1) / total ))
printf "\rProgress: %s %3d%%" "${chars[idx]}" "$i"
sleep 0.04
done
echo
Dual-metric bar (percent + throughput)
total=200
start=$(date +%s)
for ((i=1;i<=total;i++)); do
now=$(date +%s)
rate=$(( i / (now - start + 1) ))
printf "\r[%3d%%] %4d items/sec" $((i*100/total)) "$rate"
sleep 0.03
done
echo
Waveform progress bar (oscillating animation)
bar="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
len=${#bar}
for i in {0..100}; do
offset=$(( i % len ))
printf "\r[%s] %3d%%" "${bar:offset}${bar:0:offset}" "$i"
sleep 0.05
done
echo
Log-scaled progress bar (realistic for heavy workloads)
total=100
for i in $(seq 1 $total); do
scaled=$(awk "BEGIN{printf \"%d\", log($i+1)/log($total+1)*100}")
printf "\rProgress: %3d%% (realistic)" "$scaled"
sleep 0.04
done
echo
Multi-line progress bar (redraws cleanly)
total=100
start=$(date +%s)
printf "\n\n" # reserve 2 lines
for i in {1..100}; do
elapsed=$(( $(date +%s) - start ))
printf "\033[2A" # move up 2 lines
printf "[%-50s] %3d%%\n" \
"$(printf '#%.0s' $(seq 1 $((i/2))))" "$i"
printf "Elapsed: %ds\n" "$elapsed"
sleep 0.05
done