When implementing a countdown timer in Bash, we need three key features:
- Accurate time calculation (5 minutes = 300 seconds)
- Single-line display that updates in-place
- Precise 1-second intervals for updates
Here's the complete implementation that handles all requirements:
#!/bin/bash
countdown() {
seconds=$1
while [ $seconds -gt 0 ]; do
printf "\rCountdown: %02d:%02d" $((seconds/60)) $((seconds%60))
sleep 1
((seconds--))
done
echo
}
countdown 300 # 5 minutes = 300 seconds
The magic happens with these techniques:
printf "\rCountdown: %02d:%02d" # \r returns cursor to line start
sleep 1 # Precise timing
((seconds--)) # Arithmetic decrement
For production use, consider these enhancements:
#!/bin/bash
countdown() {
trap 'echo -e "\nTimer interrupted"; exit' INT
seconds=$1
while [ $seconds -gt 0 ]; do
printf "\r\033[KCountdown: %02d:%02d" $((seconds/60)) $((seconds%60))
read -t 1 -n 1 && break
((seconds--))
done
echo -e "\nTimer completed"
}
countdown 300
- Flickering display: Use \033[K to clear to end of line
- Keyboard interrupts: Trap SIGINT for clean exits
- Early termination: Add read -t for user breaks
For maximum compatibility:
printf "\rCountdown: %02d:%02d" $(($seconds/60)) $(($seconds%60))
When working with Bash scripts, sometimes we need to create timers that update in real-time without cluttering the terminal output. The key requirements for this task are:
1. Accurate 5-minute (300-second) countdown
2. Updates every second
3. Output remains on a single line
4. Clean visual display
The most efficient approach combines printf with carriage returns (\r) to overwrite the current line:
#!/bin/bash
duration=300 # 5 minutes in seconds
end_time=$(( $(date +%s) + duration ))
while [ $(date +%s) -lt $end_time ]; do
remaining=$(( end_time - $(date +%s) ))
printf "\rCountdown: %02d:%02d" $((remaining/60)) $((remaining%60))
sleep 1
done
echo -e "\nTime's up!"
For better visualization, we can add a progress bar:
#!/bin/bash
duration=300
end_time=$(( $(date +%s) + duration ))
total_width=50
while [ $(date +%s) -lt $end_time ]; do
remaining=$(( end_time - $(date +%s) ))
minutes=$((remaining/60))
seconds=$((remaining%60))
progress=$(( (duration - remaining) * total_width / duration ))
printf "\r[%-${total_width}s] %02d:%02d" \
$(printf "%${progress}s" | tr ' ' '#') \
$minutes $seconds
sleep 1
done
echo -e "\n\nTimer complete!"
For more robust scripts that handle terminal resizing:
#!/bin/bash
duration=300
end_time=$(( $(date +%s) + duration ))
cleanup() {
tput cnorm # Show cursor
exit 0
}
trap cleanup EXIT INT
tput civis # Hide cursor
while [ $(date +%s) -lt $end_time ]; do
cols=$(tput cols)
remaining=$(( end_time - $(date +%s) ))
minutes=$((remaining/60))
seconds=$((remaining%60))
# Dynamic formatting based on terminal width
if [ $cols -gt 40 ]; then
printf "\rTime remaining: %02d minutes %02d seconds " $minutes $seconds
else
printf "\r%02d:%02d " $minutes $seconds
fi
sleep 1
done
printf "\rTime's up! \n"
tput cnorm
For more terminal control:
#!/bin/bash
duration=300
end_time=$(( $(date +%s) + duration ))
while [ $(date +%s) -lt $end_time ]; do
remaining=$(( end_time - $(date +%s) ))
tput sc # Save cursor position
tput cuu1 # Move up one line
tput el # Clear to end of line
printf "%02d:%02d" $((remaining/60)) $((remaining%60))
tput rc # Restore cursor position
sleep 1
done
echo