Shellscriptのひな型
code:bash
#!/usr/bin/env bash
set -euo pipefail # Stop on errors, undefined variables, and errors in pipelines
set -o errtrace # Inherit ERR trap in functions and subshells
# ==========================================================
# Default Configuration
# ==========================================================
LOG_LEVEL="INFO"
LOG_FILE="/tmp/$(basename "${0%.sh}.log")"
# ==========================================================
# Command-line argument parsing
# ==========================================================
function usage() {
cat <<EOL
Usage: $0 options
Options:
-h, --help Show this help message and exit.
-L, --log-level Set log level (DEBUG, INFO, WARN, ERROR). Default: INFO.
-l, --log-file Set log file path. Default: logs to stdout.
EOL
}
function parse_args() {
while $# -gt 0 ; do
case "$1" in
-h|--help) usage; exit 0;;
-l|--log-file) LOG_FILE=$2; shift;;
-L|--log-level) LOG_LEVEL=$2; shift;;
*) echo "Invalid argument: $1"; usage; exit 1;;
esac
shift
done
log_debug "Log level set to $LOG_LEVEL"
log_debug "Log file set to ${LOG_FILE:-stdout}"
}
# ==========================================================
# Logging functions
# =========================================================
declare -A LOG_COLORS=(
DEBUG="\033[36m"
INFO="\033[32m"
WARN="\033[33m"
ERROR="\033[31m"
)
RESET="\033[0m"
function log() {
local level=$1
shift
local levels=("DEBUG" "INFO" "WARN" "ERROR")
local current_idx=$(printf "%s\n" "${levels@}" | grep -n "^${LOG_LEVEL}$" | cut -d: -f1)
local msg_idx=$(printf "%s\n" "${levels@}" | grep -n "^${level}$" | cut -d: -f1)
if "$msg_idx" -ge "$current_idx" ; then
echo -e "$(date +"%Y-%m-%d %H:%M:%S") [${LOG_COLORS$level}${level}${RESET}] $@"
fi
}
# Convenience functions for each log level
function log_debug() { log "DEBUG" "$@"; }
function log_info() { log "INFO" "$@"; }
function log_warn() { log "WARN" "$@"; }
function log_error() { log "ERROR" "$@"; }
# ==========================================================
# Implementation
# =========================================================
SHOULD_KEEP_RUN=true
COUNT=0
function main() {
log_info "Start!"
while ${SHOULD_KEEP_RUN} && ${COUNT} -lt 5 ; do
COUNT=$((COUNT + 1))
sleep 1;
log_debug "Sleeping 1 second... Count: ${COUNT}";
done
log_info "Finished!"
}
function sig_handler() {
SHOULD_KEEP_RUN=false
}
function cleanup() {
local rc=$?
set +e # Disable exit on error for cleanup
log_info "Cleaning up before exit with code ${rc}"
#
# Add any cleanup commands here
#
# Send EOF to log file descriptor if used
# This ensures that tee finishes writing properly
exec >&- 2>&-
exit ${rc}
}
# ==========================================================
# Main script execution
# ==========================================================
parse_args "$@"
# Redirect logs to file if specified
if "$LOG_FILE" ; then
exec > >(tee -ai ${LOG_FILE}) 2>&1
trap '' PIPE # Ignore SIGPIPE to prevent broken pipe errors
fi
# Set traps for signals
trap 'sig_handler SIGINT' SIGINT
trap 'sig_handler SIGTERM' SIGTERM
trap 'cleanup' EXIT
main