====== Bash Recipes ======
Reusable patterns for writing shell scripts: boilerplate, flags, config,
logging, error handling, and testing.
===== Skeleton =====
#!/usr/bin/env bash
set -euo pipefail # die on error, unset vars, pipe failures
IFS=$'\n\t' # optional: safer word splitting
===== Arguments and flags =====
for arg in "$@"; do
case $arg in
-v|--verbose) VERBOSE=1 ;;
-o|--output) shift; OUT=$1 ;;
*) echo "unknown: $arg" >&2; exit 1 ;;
esac
shift
done
''$0'' script name · ''$1..$9'' args · ''$@'' all args · ''$#'' count · ''$$'' PID ·
''$?'' last exit code · ''$!'' last background PID
Env-var tunables with defaults:
TURBO="${TURBO:-off}" # ${VAR:-default}
die "needs TURBO" # if unset: ${VAR:?message}
===== Conditionals =====
[[ -f file ]] [[ -d dir ]] [[ -z $str ]] [[ -n $str ]]
[[ $a == $b ]] [[ $a == foo* ]] [[ $n -gt 3 ]] # < -eq -ne -lt -le -ge
[[ $x =~ ^[0-9]+$ ]] # regex match
command -v foo >/dev/null # command exists
Files: ''-e'' exists, ''-r/-w/-x'' readable/writable/executable, ''-s'' non-empty,
''-nt''/''-ot'' newer/older.
===== Loops =====
for f in *.txt; do echo "$f"; done
for i in {1..10}; do ...; done
while IFS= read -r line; do ...; done < file
find . -type f -print0 | while IFS= read -r -d '' f; do ...; done # safe
===== Functions / output / errors =====
die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
say() { printf '==> %s\n' "$*"; }
trap 'echo "interrupted"; exit 130' INT
trap cleanup EXIT # run on any exit
Return values: ''return 1'' sets ''$?''. Get output: ''result="$(myfunc)"''.
===== Strings =====
${s#prefix} ${s%suffix} # strip shortest
${s##*/} # basename
${s%/*} # dirname
${s/old/new} ${s//o/n} # replace first / all
${#s} # length
${s:2:5} # substring
${s^^} ${s,,} # upper / lower
===== Arrays =====
a=(one two three)
echo "${a[1]}" "${a[@]}" "${#a[@]}"
a+=(four)
mapfile -t lines < file # file -> array
===== Heredocs =====
cat <
===== Debugging =====
bash -x script.sh # trace every command
shellcheck script.sh # linter: catches real bugs
set -x; ... ; set +x # trace a section
===== Gotchas =====
* [[ not ''['': ''['' is a command with worse semantics.
* Quote everything: ''"$var"'': unquoted vars split on spaces.
* ''cd'' in a script doesn't affect your shell; use ''( cd dir && ... )'' subshells.
* ''local'' only exists inside functions.
* ''exit'' inside ''$(...)'' exits the subshell, not the script.
* ''pipefail'' + ''set -o'' at top or ''foo | grep x'' returns 0 even when foo fails.
===== Reading input =====
read -rp "Name? " name
read -rsp "Password: " pw; echo # -s silent
read -t 5 -n1 -p "continue? " || exit
===== Running as root =====
[[ $EUID -ne 0 ]] && exec sudo -E bash "$0" "$@" # re-exec via sudo
===== See Also =====
* [[terminal:scripting:start|Scripting Index]]
* [[terminal:tools:start|CLI Tools]]
* [[terminal:start|Return to terminal]]
* [[start|Return to wiki home]]