示例说明
脚本中途被 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 "$@"