| name | shell-scripting-fundamentals |
| user-invocable | false |
| description | Use when writing or modifying Bash/shell scripts. Covers script structure, variables, quoting, conditionals, and loops with modern best practices. |
| allowed-tools | ["Read","Write","Edit","Bash","Grep","Glob"] |
Shell Scripting Fundamentals
Core patterns and best practices for writing robust, maintainable shell scripts.
Script Structure
Always start scripts with a proper shebang and safety options:
#!/usr/bin/env bash
set -euo pipefail
Safety Options Explained
set -e: Exit on any command failure
set -u: Error on undefined variables
set -o pipefail: Pipeline fails if any command fails
Variables
Declaration and Assignment
name="value"
readonly CONFIG_DIR="/etc/myapp"
my_function() {
local result="computed"
echo "$result"
}
Always Quote Variables
echo "$variable"
cp "$source" "$destination"
echo $variable
cp $source $destination
Default Values
name="${NAME:-default}"
name="${NAME:-}"
: "${NAME:=default}"
: "${REQUIRED_VAR:?Error: REQUIRED_VAR must be set}"
Conditionals
Test Syntax
if [[ -f "$file" ]]; then
echo "File exists"
fi
if [[ "$string" == "value" ]]; then
echo "Match"
fi
if (( count > 10 )); then
echo "Greater than 10"
fi
if [[ "$input" =~ ^[0-9]+$ ]]; then
echo "Numeric input"
fi
Common Test Operators
| Operator | Description |
|---|
-f | File exists and is regular file |
-d | Directory exists |
-e | Path exists |
-r | Readable |
-w | Writable |
-x | Executable |
-z | String is empty |
-n | String is not empty |
Loops
For Loops
for item in one two three; do
echo "$item"
done
for file in *.txt; do
[[ -e "$file" ]] || continue
process "$file"
done
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
While Loops
while IFS= read -r line; do
echo "$line"
done < "$filename"
while IFS= read -r line; do
echo "$line"
done < <(some_command)
Arrays
declare -a files=()
files+=("file1.txt")
files+=("file2.txt")
for file in "${files[@]}"; do
echo "$file"
done
echo "${#files[@]}"
echo "${files[0]}"
Command Substitution
result=$(command)
result=$(echo $(date))
result=`command`
Functions
process_file() {
local file="$1"
local output_dir="${2:-./output}"
if [[ ! -f "$file" ]]; then
echo "Error: File not found: $file" >&2
return 1
fi
cp "$file" "$output_dir/"
}
process_file "input.txt" "/tmp/output"
Best Practices Summary
- Always use
#!/usr/bin/env bash for portability
- Enable strict mode:
set -euo pipefail
- Quote all variable expansions
- Use
[[ ]] instead of [ ] for tests
- Use
$(command) instead of backticks
- Declare local variables in functions
- Use arrays for lists of items
- Check command existence before use:
command -v cmd >/dev/null