← 中文资源

简体中文版本

TRAP 清理与错误处理

用 EXIT 陷阱保证任何退出路径都执行清理:失败时删除半成品工作目录,成功时保留产物,INT 与 TERM 映射标准退出码。

阅读英文原文

示例说明

脚本中途被 Ctrl-C 或 kill 时,已创建的临时目录就是垃圾——EXIT 陷阱在解释器退出前必然执行,是清理的唯一可靠挂点。FINISHED 标志区分两种退出:成功路径把产物移到目标位置后才置位,失败与中断路径都走删除分支。INT 与 TERM 的陷阱把它们转成 exit 调用,让 EXIT 陷阱连带触发——信号本身不跑 EXIT 陷阱。

技术要点

  • EXIT 陷阱覆盖全部退出路径
  • FINISHED 标志区分成败清理策略
  • 信号陷阱转发触发 EXIT 链

代码解读

convert_dataset 的每步都接 die——Error 前缀的消息与退出码一并上抛,EXIT 陷阱随即清理半成品目录;反斜杠续行让长命令保持可读。main 校验输入存在后调用转换,成功后先置 FINISHED 再移动产物——顺序反了会导致移动失败时误删成果。130 与 143 是 SIGINT 与 SIGTERM 的惯例退出码。

使用建议

清理函数里只做幂等操作,自身失败不应掩盖主错误。并发脚本共享目录时清理要带属主标记。生产脚本建议引入 mktemp 的模板参数防撞名。

代码示例

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

WORKDIR=""
FINISHED=false

cleanup() {
  local exit_code=$?
  if [[ "$FINISHED" == false && -n "$WORKDIR" && -d "$WORKDIR" ]]; then
    rm -rf "$WORKDIR"
    echo "Error: 未完成的工作目录已清理: ${WORKDIR}" >&2
  fi
  echo "cleanup done, exit ${exit_code}"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

die() {
  echo "Error: $*" >&2
  exit 1
}

convert_dataset() {
  WORKDIR=$(mktemp -d) || die "无法创建临时目录"
  local input="$1"

  gzip -dc "$input" > "$WORKDIR/raw.json" \
    || die "解压失败: ${input}"

  jq -c '.[]' "$WORKDIR/raw.json" > "$WORKDIR/records.ndjson" \
    || die "JSON 转换失败"

  wc -l < "$WORKDIR/records.ndjson"
}

main() {
  [[ -f "$1" ]] || die "输入文件不存在: ${1}"
  local lines
  lines=$(convert_dataset "$1")
  echo "converted ${lines} records"

  FINISHED=true   # 成功路径:保留产物目录给调用方
  mv "$WORKDIR/records.ndjson" ./out.ndjson
}

main "$@"