पाठ 22 / 25
A Production Script Template
Start every serious script from a template with strict mode, logging, usage and cleanup.
Good habits in one file
Most of this course fits into a reusable template. Start with #!/usr/bin/env bash and set -Eeuo pipefail. Resolve the script's own directory so it works from anywhere. Define small helpers: log and die writing timestamped messages to stderr, require_command for dependencies. Parse options with getopts and print usage. Create temporary space with mktemp -d and remove it in an EXIT trap, plus an ERR trap that reports the failing line. Put the main logic in a main function called at the bottom with main "$@", so the whole file is parsed before anything runs, which also protects against a script being edited while it executes. Support --dry-run for anything destructive, make the script idempotent (running it twice is safe), and keep it ShellCheck-clean. With this skeleton, a new script starts safe instead of being made safe later.
The skeleton of a robust script
Header, helpers, option parsing, traps and a main function, in a fixed order.
Template
Copy, rename and fill in main.
#!/usr/bin/env bash
# rotate-logs.sh - compress and prune application logs
set -Eeuo pipefail
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
log() { printf '%s %s\n' "$(date -u +%FT%TZ)" "$*" >&2; }
die() { log "ERROR: $*"; exit 1; }
require_command() { for c in "$@"; do command -v "$c" > /dev/null || die "missing: $c"; done; }
usage() { printf 'usage: %s [-n] [-d DAYS] DIR\n' "${0##*/}"; }
main() {
local dry_run=0 days=14 opt
while getopts ":nd:h" opt; do
case $opt in
n) dry_run=1 ;;
d) days=$OPTARG ;;
h) usage; return 0 ;;
*) usage >&2; return 2 ;;
esac
done
shift $(( OPTIND - 1 ))
local dir="${1:?$(usage)}"
require_command find gzip
workdir=$(mktemp -d)
trap 'rm -rf "${workdir:?}"' EXIT
trap 'log "failed at line $LINENO: $BASH_COMMAND"' ERR
log "compressing logs older than 1 day in $dir"
if (( dry_run )); then
find "$dir" -name '*.log' -mtime +1 -print
else
find "$dir" -name '*.log' -mtime +1 -exec gzip -- {} +
find "$dir" -name '*.log.gz' -mtime +"$days" -delete
fi
log "done"
}
main "$@"Idempotency makes scripts safe to rerun
Use mkdir -p, check before creating users or files, and compare desired and current state. When a script fails halfway, the fix should be to run it again, not to clean up by hand.
त्वरित जाँच: Why wrap a script's logic in a main function called at the end with main "$@"?
- Bash requires a main function
- It makes the script run faster
- The whole file is parsed before anything runs, and the structure is clearer
- It disables set -e
Answer
The whole file is parsed before anything runs, and the structure is clearer — Calling main at the bottom ensures every function is defined first and guards against partial execution.