Reading — step 1 of 5
Learn
~1 min readPerformance and Tools
Real Bash projects use functions in libraries, sourced from main scripts. The patterns:
Library files — define functions, no top-level execution:
# lib/log.sh
log_info() { echo "[INFO] $*" >&2; }
log_error() { echo "[ERROR] $*" >&2; }
source (or .) loads them:
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/log.sh"
log_info "starting"
Idempotent loading — guard against double-source:
# lib/log.sh
if [[ -n "${__LIB_LOG_SOURCED:-}" ]]; then return; fi
__LIB_LOG_SOURCED=1
log_info() { ... }
Subcommand pattern — git-like CLIs:
#!/bin/bash
cmd="${1:-help}"
shift || true
cmd_init() { echo "initializing..."; }
cmd_build() { echo "building $@"; }
cmd_help() { echo "usage: $0 {init|build|help}"; }
if declare -F "cmd_$cmd" > /dev/null; then
"cmd_$cmd" "$@"
else
cmd_help
exit 1
fi
declare -F name checks if a function exists. Combined with naming conventions, you get extensible CLIs.
Argument parsing with getopts:
verbose=0
output="-"
while getopts "vo:h" opt; do
case $opt in
v) verbose=1 ;;
o) output="$OPTARG" ;;
h) usage; exit 0 ;;
*) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1)) # remove parsed options
# remaining args in $@
getopts handles -v, -o file, -vo file, etc. For long options (--verbose), use getopt (different tool, less portable) or parse manually.
Trap-based main:
main() {
parse_args "$@"
setup
do_work
}
main "$@"
Cleanly separates everything from top-level code, makes the script's flow obvious.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…