← Skills
Compatibility and readability conventions for shell scripts.
#!/usr/bin/env bash for portability, not hardcoded /bin/bash#!/bin/sh only when the script genuinely works with POSIX shStart scripts with:
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
-e - exit on error-u - error on unset variables-o pipefail - fail if any command in a pipeline failsIFS - only split on newlines and tabs, avoids word-splitting surprisessnake_case for variables and functionsUPPER_SNAKE_CASE for environment variables and readonly globalsif [[ -f "$file" ]]; then
echo "exists"
fi
for item in "${list[@]}"; do
process "$item"
done
[[ ]] over [ ] - fewer quoting issues, more features (regex, pattern matching)"$var", not $var"$(command)"name() { … } - no function keyword (POSIX-compatible)local for all function-scoped variables:greet() {
local name="$1"
echo "Hello, $name"
}
die() {
echo "$*" >&2
exit 1
}
set -e isn’t enoughtrap for cleanup on exit:cleanup() {
rm -f "$tmpfile"
}
trap cleanup EXIT
if [[ $# -lt 1 ]]; then
die "Usage: $0 <file>"
fi
${var:-default} for optional variables with fallbacksprintf over echo for anything beyond plain strings (more portable)cat <<EOF > config.ini
[default]
path=$target_dir
mode=0755
EOF
#!/usr/bin/env bash
set -euo pipefail
# ── Constants ──────────────────────────────
readonly APP_NAME="my-tool"
readonly CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/$APP_NAME"
# ── Helpers ────────────────────────────────
die() { echo "$*" >&2; exit 1; }
# ── Main ───────────────────────────────────
main() { ... }
main "$@"
main "$@" at the bottom - keeps the global scope clean