Advanced Shell Programming
Master advanced techniques: arrays, parameter expansion, traps, and subshells.
Arrays
Arrays help you manage lists without fighting word-splitting. Use "${arr[@]}" to preserve elements safely.
files=(*.txt)
echo "${files[0]}" # First element
echo "${files[@]}" # All elements
echo "${#files[@]}" # Array length
echo "${files[0]}" # First element
echo "${files[@]}" # All elements
echo "${#files[@]}" # Array length
Associative arrays (key → value)
Great for lookups (like a tiny dictionary):
declare -A ports
ports[ssh]=22
ports[https]=443
echo "SSH: ${ports[ssh]}"
ports[ssh]=22
ports[https]=443
echo "SSH: ${ports[ssh]}"
Reading files safely (mapfile)
Avoid for x in $(cat file) (it breaks on spaces). Use mapfile or while read.
mapfile -t lines < hosts.txt
for host in "${lines[@]}"; do
echo "Pinging $host"
done
for host in "${lines[@]}"; do
echo "Pinging $host"
done
Parameter Expansion
Parameter expansion is Bash’s “string toolbox”: defaults, trimming, substring, replacements.
file="document.tar.gz"
echo "${file%.gz}" # document.tar
echo "${file##*.}" # gz
echo "${file/tar/zip}" # document.zip.gz
echo "${file%.gz}" # document.tar
echo "${file##*.}" # gz
echo "${file/tar/zip}" # document.zip.gz
# Defaults + required vars
: "${ENVIRONMENT:?Missing ENVIRONMENT}"
timeout="${TIMEOUT:-30}"
# Substring
echo "${file:0:8}" # document
: "${ENVIRONMENT:?Missing ENVIRONMENT}"
timeout="${TIMEOUT:-30}"
# Substring
echo "${file:0:8}" # document
Subshells & Process Substitution
Parentheses run a command list in a subshell. Process substitution (<(cmd)) lets you treat command output like a file.
# Subshell: changes don't affect parent shell
( cd /tmp && touch testfile )
pwd # still your original directory
# Compare two command outputs
diff <(sort a.txt) <(sort b.txt)
( cd /tmp && touch testfile )
pwd # still your original directory
# Compare two command outputs
diff <(sort a.txt) <(sort b.txt)
Traps
Traps run cleanup logic on exit or signals. This is essential for scripts that create temp files or lock files.
cleanup() { rm -f /tmp/myfile; }
trap cleanup EXIT
trap cleanup EXIT
Better cleanup with mktemp
tmp="$(mktemp)"
cleanup() { rm -f "$tmp"; }
trap cleanup EXIT
echo "work" > "$tmp"
cleanup() { rm -f "$tmp"; }
trap cleanup EXIT
echo "work" > "$tmp"
File Descriptors and Redirection
Power scripts often separate stdout (data) from stderr (logs/errors). This makes pipelines predictable.
# Send logs to stderr
log() { echo "[INFO] $*" >&2; }
log "Starting"
# Redirect stderr to a file
command 2> errors.log
log() { echo "[INFO] $*" >&2; }
log "Starting"
# Redirect stderr to a file
command 2> errors.log
✅ Practice (20 minutes)
- Use an associative array to map service names to ports and print them.
- Create a script that uses
mktemp+trapto safely handle temp files. - Use process substitution to diff sorted outputs of two files.