Shell Scripting Expert
Expert guidance for writing robust, maintainable Bash and Zsh scripts with best practices for automation and command-line tool usage.
Script Structure Essentials
Start every script with:
bash
#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t'
- •
set -e: Exit on error - •
set -u: Error on undefined variables - •
set -o pipefail: Catch errors in pipes - •
IFS=$'\n\t': Safer word splitting
Critical Best Practices
- •Always quote variables:
"$variable"not$variable - •Use
[[for conditionals (Bash):if [[ "$var" == "value" ]]; then - •Check command existence:
if command -v git &> /dev/null; then - •Avoid parsing
ls: Use globs orfindinstead - •Use arrays for lists:
files=("file1" "file2")not space-separated strings - •Handle errors with traps:
bash
trap cleanup EXIT trap 'echo "Error on line $LINENO"' ERR
Common Patterns
Argument Parsing
bash
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help) usage; exit 0 ;;
-v|--verbose) VERBOSE=true; shift ;;
-*) echo "Unknown option: $1"; exit 1 ;;
*) break ;;
esac
done
Safe File Iteration
bash
# Prefer this (handles spaces, newlines correctly):
while IFS= read -r -d '' file; do
echo "Processing: $file"
done < <(find . -type f -name "*.txt" -print0)
# Or with simple globs:
for file in *.txt; do
[[ -e "$file" ]] || continue # Skip if no matches
echo "Processing: $file"
done
User Confirmation
bash
read -rp "Continue? [y/N] " response
if [[ "$response" =~ ^[Yy]$ ]]; then
echo "Continuing..."
fi
Colored Output
bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${GREEN}Success${NC}"
echo -e "${RED}Error${NC}" >&2
Modern Tool Alternatives
When appropriate, suggest these modern replacements:
- •
ripgrep(rg) → faster than grep - •
fd→ faster than find - •
fzf→ interactive filtering - •
jq→ JSON processing - •
yq→ YAML processing - •
bat→ cat with syntax highlighting - •
eza→ enhanced ls
Function Organization
bash
usage() {
cat <<EOF
Usage: ${0##*/} [OPTIONS] <args>
Description of what this script does.
OPTIONS:
-h, --help Show this help
-v, --verbose Verbose output
EOF
}
main() {
# Main logic here
:
}
Snakemake & Shells
- •Snakemake uses Bash: By default, Snakemake executes
shell:directives using/bin/bash -euo pipefail. - •Write Bash, not Fish: Even if your terminal is Fish, write rule commands in standard Bash syntax.
- •Strict Mode: Snakemake enables strict mode automatically.
Zsh-Specific Features
When user specifies Zsh:
- •Advanced globbing:
**/*.txt(recursive),*.txt~*test*(exclude pattern) - •Parameter expansion:
${var:u}(uppercase),${var:l}(lowercase) - •Associative arrays:
typeset -A hash; hash[key]=value - •Extended globbing: Enable with
setopt extended_glob
Security Considerations
- •Never
evaluntrusted input - •Validate user input before use
- •Use
mktempfor temporary files:TEMP_FILE=$(mktemp) - •Be explicit with
rm -rfoperations - •Check for TOCTOU (Time-Of-Check-Time-Of-Use) race conditions
- •Don't store secrets in scripts; use environment variables or secret managers
Performance Tips
- •Use built-ins over external commands (
[[ ]]vstest,$(( ))vsexpr) - •Avoid unnecessary subshells:
var=$(cat file)→var=$(<file) - •Use
readnotcat | while:while read -r line; do ... done < file - •Consider
xargs -Por GNUparallelfor parallel processing
Quick Reference Template
See references/template.sh for a complete, production-ready script template with all best practices incorporated.