Writing shell scripts is easy, but writing good shell scripts that are reliable and secure is an art. These best practices will help you avoid common pitfalls and make your scripts "production-grade."
1. Use a Modern Shebang
Instead of #!/bin/bash, use #!/usr/bin/env bash. This makes your script more portable across different Linux distributions and Unix systems.
2. Quoting Variables (Always!)
Unquoted variables can lead to "word splitting" and "globbing" issues.
Bad:
FILE_NAME="my file.txt"
rm $FILE_NAME # This will try to delete "my" and "file.txt" separately!Good:
FILE_NAME="my file.txt"
rm "$FILE_NAME" # Always double-quote variable expansions.3. Use [[ ]] instead of [ ]
[[ ]] is a Bash-specific (and Zsh) upgrade to the POSIX [ ] (also known as test). It's more powerful and less likely to cause errors.
| Feature | [ ] | [[ ]] |
|---|---|---|
| Logic | -a, -o | &&, ` |
| Pattern Matching | No | Yes (Regex support) |
| Word Splitting | Yes (Problematic) | No (Safe) |
Example:
if [[ $NAME == "DevOps" && $STATUS == "Ready" ]]; then
echo "Go!"
fi4. Descriptive Variable Names
Avoid single-letter variables like i, x, or y except for very short loops. Use UPPERCASE for global constants and lowercase for local variables.
# Good
readonly LOG_FILE="/var/log/app.log"
local_count=05. Security: Handling Secrets
NEVER hardcode API keys or passwords in scripts.
Avoid:
export GITHUB_TOKEN="ghp_1234567890abcdef"Best Practice:
- Use environment variables (from CI/CD tools like GitHub Actions secrets).
- Use a secret manager (like AWS Secrets Manager, HashiCorp Vault).
- Prompt the user (using
read -s).
6. Make Scripts Idempotent
An idempotent script can be run multiple times and will only make changes when necessary, without causing errors.
Bad:
mkdir my_dir # Fails if directory existsGood:
mkdir -p my_dir # Continues silently if directory exists7. Temporary Files
Always use mktemp for creating temporary files instead of hardcoded names like /tmp/temp.txt. This prevents security risks and collisions.
# Create a secure temporary file
tmpfile=$(mktemp)
# Register a cleanup function to delete it on exit
trap 'rm -f "$tmpfile"' EXIT
echo "processing data..." > "$tmpfile"Summary: The Golden Rules
- Always double-quote your variables.
- Use
set -euo pipefailat the top. - Use ShellCheck to lint your code.
- Keep functions small and modular.
- Use
localfor variables inside functions.