| name | bash-development |
| description | This skill should be used when the user asks to "write a bash script", "create a shell script", "implement bash function", "parse arguments in bash", "handle errors in bash", or mentions bash development, shell scripting, script templates, or modern bash patterns. |
Bash Development
Core patterns and best practices for Bash 5.1+ script development. Provides modern bash idioms, error handling, argument parsing, and pure-bash alternatives to external commands.
Script Foundation
Every script starts with the essential header:
#!/usr/bin/env bash
set -euo pipefail
set options explained:
-e - Exit immediately on command failure
-u - Treat unset variables as errors
-o pipefail - Pipeline fails if any command fails
Script Metadata Pattern
SCRIPT_NAME=$(basename "${BASH_SOURCE[0]}")
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly SCRIPT_VERSION="1.0.0"
readonly SCRIPT_NAME SCRIPT_DIR
Error Handling
Implement trap-based error handling for robust scripts:
handle_error() {
local line="${1}"
local exit_code="${2:-1}"
printf '%s\n' "Error on line ${line}" >&2
exit "${exit_code}"
}
trap 'handle_error ${LINENO} $?' ERR
cleanup() {
rm -f "${TEMP_FILE:-}"
}
trap cleanup EXIT
Argument Parsing
Standard argument parsing template:
usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} [OPTIONS] <argument>
Options:
-h, --help Show this help message
-v, --version Show version information
-d, --debug Enable debug mode
-f, --file Specify input file
Examples:
${SCRIPT_NAME} file.txt
${SCRIPT_NAME} --debug file.txt
EOF
}
main() {
local debug=0
local input_file=""
while [[ $# -gt 0 ]]; do
case "${1}" in
-h|--help) usage; exit 0 ;;
-v|--version) printf '%s version %s\n' "${SCRIPT_NAME}" "${SCRIPT_VERSION}"; exit 0 ;;
-d|--debug) debug=1; set -x; shift ;;
-f|--file) input_file="${2}"; shift 2 ;;
-*) printf 'Unknown option: %s\n' "${1}" >&2; usage; exit 1 ;;
*) break ;;
esac
done
if [[ $# -lt 1 ]]; then
printf 'Missing required argument\n' >&2
usage
exit 1
fi
}
main
Variable Best Practices
Always use curly braces and quote variables:
"${variable}"
"${array[@]}"
$variable
${array[*]}
Use readonly for constants:
readonly CONFIG_FILE="/etc/app/config"
readonly -a VALID_OPTIONS=("opt1" "opt2" "opt3")
Note: Never use readonly in sourced scripts - it causes errors on re-sourcing.
String Operations (Pure Bash)
Prefer native bash parameter expansion over external tools:
trimmed="${string#"${string%%[![:space:]]*}"}"
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
lower="${string,,}"
upper="${string^^}"
substring="${string:0:10}"
suffix="${string: -5}"
replaced="${string//old/new}"
replaced="${string/old/new}"
no_prefix="${string#prefix}"
no_prefix="${string##*/}"
no_suffix="${string%suffix}"
no_suffix="${string%%/*}"
Array Operations
declare -a indexed_array=()
declare -A assoc_array=()
shopt -s nullglob
for file in *.txt; do
process "${file}"
done
shopt -u nullglob
length="${#array[@]}"
array+=("new_element")
for i in "${!array[@]}"; do
printf '%d: %s\n' "${i}" "${array[i]}"
done
File Operations
content="$(<"${file}")"
mapfile -t lines < "${file}"
[[ -f "${file}" ]]
[[ -d "${dir}" ]]
[[ -r "${file}" ]]
[[ -w "${file}" ]]
[[ -x "${file}" ]]
[[ -s "${file}" ]]
temp_file=$(mktemp)
trap 'rm -f "${temp_file}"' EXIT
Conditional Expressions
Use [[ ]] for conditionals (bash-specific, more powerful):
[[ "${var}" == "value" ]]
[[ "${var}" == pattern* ]]
[[ "${var}" =~ ^regex$ ]]
(( num > 10 ))
[[ "${num}" -gt 10 ]]
[[ -f "${file}" && -r "${file}" ]]
[[ "${opt}" == "a" || "${opt}" == "b" ]]
Utility Functions
command_exists() {
command -v "${1}" >/dev/null 2>&1
}
get_script_dir() {
local source="${BASH_SOURCE[0]}"
while [[ -L "${source}" ]]; do
local dir=$(cd -P "$(dirname "${source}")" && pwd)
source=$(readlink "${source}")
[[ "${source}" != /* ]] && source="${dir}/${source}"
done
cd -P "$(dirname "${source}")" && pwd
}
run_privileged() {
if [[ "${EUID}" -eq 0 ]]; then
"$@"
elif command_exists sudo; then
sudo
>&2
1
}
Performance Guidelines
- Use builtins over external commands when possible
- Batch operations instead of loops for large datasets
- Use
printf over echo for portability and control
- Avoid unnecessary subshells in tight loops
- Use
[[ ]] over [ ] for string comparisons
Additional Resources
Reference Files
For detailed patterns and examples: