创建可复用的Bash脚本模板以提高效率

构建可复用的Bash脚本模板,包含严格模式、使用说明、日志记录、清理机制以及安全的getopts参数解析。

创建可复用的Bash脚本模板以提高效率

可复用的Bash脚本模板能帮助你在每次自动化任务时,避免重复编写相同的安全检查。一个好的模板能为脚本提供严格的错误处理、可预测的日志记录、使用帮助、参数解析以及清理钩子,然后再添加项目特定的逻辑。

目标不是构建一个庞大的框架,而是一个小巧的起点,让日常脚本保持一致且更易于调试。

为什么使用脚本模板?

当你的团队编写大量用于备份、部署、报告、日志清理或数据迁移的小脚本时,模板非常有用。如果没有模板,每个脚本处理错误、选项和日志的方式往往各不相同。

一个可复用的模板能为你提供:

  • 一致的严格模式和引用习惯。
  • 标准的usage函数。
  • 统一的日志辅助函数。
  • 用于临时文件和锁的清理钩子。
  • 使用getopts进行可预测的命令行解析。

核心模板组件

从Bash本身开始,然后开启严格行为:

#!/usr/bin/env bash
set -euo pipefail

set -e 会在许多未处理的命令失败时退出,set -u 将未设置的变量视为错误,set -o pipefail 能捕获管道中的失败。你仍然需要对预期中的失败进行显式检查,但严格模式能捕获许多意外错误。

添加脚本名称和简短的使用说明块:

readonly SCRIPT_NAME="$(basename "$0")"

usage() {
    cat <<USAGE
用法: $SCRIPT_NAME [-v] [-o output_dir] input_file

选项:
  -v              启用详细日志
  -o output_dir   将输出写入此目录
  -h              显示此帮助信息
USAGE
}

使用小型日志辅助函数,而不是散落的echo调用:

log_info() {
    [[ "$VERBOSE" -eq 1 ]] && printf '[INFO] %s\n' "$1"
}

die() {
    printf '[ERROR] %s\n' "$1" >&2
    exit 1
}

如果脚本创建临时文件或锁文件,请尽早添加清理函数:

cleanup() {
    # 在此处删除临时文件或释放锁。
    :
}

trap cleanup EXIT

使用getopts进行安全的参数解析

大多数可复用模板需要标志。Bash内置的getopts足以处理像-v-o output_dir这样的短选项。

VERBOSE=0
OUTPUT_DIR="."

while getopts ":vo:h" opt; do
    case "$opt" in
        v) VERBOSE=1 ;;
        o) OUTPUT_DIR=$OPTARG ;;
        h) usage; exit 0 ;;
        :) die "选项 -$OPTARG 需要参数" ;;
        \?) die "未知选项: -$OPTARG" ;;
    esac
done
shift "$((OPTIND - 1))"

INPUT_FILE=${1:-}
[[ -n "$INPUT_FILE" ]] || { usage >&2; exit 1; }
[[ -f "$INPUT_FILE" ]] || die "未找到输入文件: $INPUT_FILE"

":vo:h" 开头的冒号让脚本自行处理缺少选项参数的情况。解析后,shift "$((OPTIND - 1))" 移除已解析的选项,使位置参数更易读取。

一个可复用的紧凑模板

以下是一个适用于小型操作脚本的实用起点:

#!/usr/bin/env bash
set -euo pipefail

readonly SCRIPT_NAME="$(basename "$0")"
VERBOSE=0
OUTPUT_DIR="."

usage() {
    cat <<USAGE
用法: $SCRIPT_NAME [-v] [-o output_dir] input_file
USAGE
}

log_info() {
    [[ "$VERBOSE" -eq 1 ]] && printf '[INFO] %s\n' "$1"
}

die() {
    printf '[ERROR] %s\n' "$1" >&2
    exit 1
}

cleanup() {
    :
}
trap cleanup EXIT

while getopts ":vo:h" opt; do
    case "$opt" in
        v) VERBOSE=1 ;;
        o) OUTPUT_DIR=$OPTARG ;;
        h) usage; exit 0 ;;
        :) die "选项 -$OPTARG 需要参数" ;;
        \?) die "未知选项: -$OPTARG" ;;
    esac
done
shift "$((OPTIND - 1))"

INPUT_FILE=${1:-}
[[ -n "$INPUT_FILE" ]] || { usage >&2; exit 1; }
[[ -f "$INPUT_FILE" ]] || die "未找到输入文件: $INPUT_FILE"
mkdir -p "$OUTPUT_DIR"

log_info "正在处理 $INPUT_FILE"
# 在此处添加你的脚本逻辑。

保持模板简短。只有当多个脚本确实需要时,才添加共享函数。模板应该消除重复的设置工作,而不是让每个脚本都感觉像是一个框架。