Colour and formatting transform a wall of text into something operators can scan at a glance during incidents. Good terminal UI is not decoration — it reduces time-to-diagnosis and makes scripts feel trustworthy. These patterns use standard ANSI codes that work in every modern terminal.
1
ANSI colour codes and text attributes
BASH
# ── Colour constants ──────────────────────────────────────
# Use $'...' quoting for escape sequences
readonly RESET=$'\033[0m'
readonly BOLD=$'\033[1m'
readonly DIM=$'\033[2m'
readonly UNDERLINE=$'\033[4m'
# Foreground colours
readonly BLACK=$'\033[30m' RED=$'\033[31m' GREEN=$'\033[32m'
readonly YELLOW=$'\033[33m' BLUE=$'\033[34m' MAGENTA=$'\033[35m'
readonly CYAN=$'\033[36m' WHITE=$'\033[37m'
# Bright foreground colours
readonly BRED=$'\033[91m' BGREEN=$'\033[92m' BYELLOW=$'\033[93m'
readonly BBLUE=$'\033[94m' BWHITE=$'\033[97m'
# ── Usage ─────────────────────────────────────────────────
echo "${GREEN}✔ Success${RESET}"
echo "${RED}✘ Error${RESET}"
echo "${YELLOW}⚠ Warning${RESET}"
echo "${BOLD}${BLUE}INFO${RESET}: Deployment starting"
echo "${DIM}[2026-05-01 10:14:02]${RESET} Processing..."
# ── Detect if terminal supports colour ────────────────────
supports_colour() {
[[ -t 1 ]] && [[ "$(tput colors 2>/dev/null)" -ge 8 ]]
}
# Disable colour when piped or redirected
if ! supports_colour; then
RED=""; GREEN=""; YELLOW=""; RESET=""; BOLD=""
fi
# Or respect NO_COLOR env variable (https://no-color.org)
[[ -n "${NO_COLOR:-}" ]] && RED="" GREEN="" RESET=""
2
Formatted output with printf
BASH
# ── Aligned columns ───────────────────────────────────────
printf "%-20s %-15s %-8s %s\n" "HOST" "STATUS" "DISK" "UPTIME"
printf "%-20s %-15s %-8s %s\n" "────────────────────" "───────────────" "────────" "──────"
for srv in web-01 web-02 db-01; do
status="${GREEN}UP${RESET}"
printf "%-20s %-24s %-8s %s\n" \
"${srv}" "${status}" "38%" "14d 3h"
done
# ── Progress bar ──────────────────────────────────────────
progress_bar() {
local pct="${1}"
local width=40
local filled=$(( pct * width / 100 ))
local empty=$(( width - filled ))
printf "\r [${GREEN}%${filled}s${RESET}%${empty}s] %3d%%" \
"$(printf '#%.0s' $(seq 1 "${filled}"))" \
"" "${pct}"
}
for i in {0..100..5}; do
progress_bar "${i}"
sleep 0.1
done
echo ""
# ── Spinner ───────────────────────────────────────────────
spin() {
local msg="${1}"
local pid="${2}"
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
local i=0
while kill -0 "${pid}" 2>/dev/null; do
printf "\r ${CYAN}%s${RESET} %s" \
"${frames[$((i % ${#frames[@]}))]}" "${msg}"
(( i++ ))
sleep 0.1
done
printf "\r ${GREEN}✔${RESET} %s\n" "${msg}"
}
long_task &
spin "Running long task..." $!
3
Coloured log functions library
BASH
#!/usr/bin/env bash
# Coloured logging functions for any script
readonly _R=$'\033[0m' # reset
readonly _B=$'\033[1m' # bold
readonly _G=$'\033[32m' # green
readonly _Y=$'\033[33m' # yellow
readonly _RE=$'\033[31m' # red
readonly _C=$'\033[36m' # cyan
readonly _D=$'\033[2m' # dim
log_info() { printf "%b INFO%b %s\n" "${_C}${_B}" "${_R}" "$*"; }
log_ok() { printf "%b OK %b %s\n" "${_G}${_B}" "${_R}" "$*"; }
log_warn() { printf "%b WARN %b %s\n" "${_Y}${_B}" "${_R}" "$*"; }
log_error() { printf "%b ERROR%b %s\n" "${_RE}${_B}" "${_R}" "$*" >&2; }
log_dim() { printf "%b %s%b\n" "${_D}" "$*" "${_R}"; }
log_step() { printf "\n%b▸ %s%b\n" "${_B}" "$*" "${_R}"; }
# Usage
log_step "Deployment Phase 1: Validation"
log_info "Checking prerequisites..."
log_ok "mysql client found"
log_warn "Disk 78% full — recommend cleanup"
log_error "DB_PASS not set"
log_dim "[2026-05-01 10:14:02] Debug: connecting to prod-db-01:3306"
vriddh@prod-01:~/scripts$./deploy.sh
▸ Deployment Phase 1: Validation
INFO Checking prerequisites...
OK mysql client found
OK curl found
WARN Disk 78% full — recommend cleanup
▸ Deployment Phase 2: Deploy
INFO Uploading application files...
[████████████████████████████████ ] 82%
█
⚠ When not to use colour — Always check
[[ -t 1 ]] (stdout is a terminal) before emitting colour codes. Colour in logs files makes them hard to grep. Respect the NO_COLOR environment variable. Provide a --no-colour flag in any user-facing tool. Scripts piped into others should produce clean output.✔ Terminal UI rules — Store colour codes in
readonly variables with $'...' quoting. Always end coloured output with ${RESET}. Use printf over echo -e for portability. For progress bars, use \r to overwrite the current line. Check terminal support with [[ -t 1 ]] and disable colour when stdout is not a tty.