Shell 脚本别图快,这几个习惯能救你

举报
yd_252271337 发表于 2026/11/11 08:41:12 2026/11/11
【摘要】 # Shell 脚本别图快,这几个习惯能救你几年前我写过一条清理脚本,大概是这样的:```bashrm -rf $TARGET_DIR/*```平时跑没事。有一次变量因为前一步失败空了,展开成了 `rm -rf /*`。好在当时是在测试机,而且 `rm` 的保护拦住了一部分,但那一刻我后背是凉的。从那以后,我写 shell 都会先把「别再出这种事」放第一位。## 第一件事:打开严格模式脚本开...

# Shell 脚本别图快,这几个习惯能救你

几年前我写过一条清理脚本,大概是这样的:

```bash
rm -rf $TARGET_DIR/*
```

平时跑没事。有一次变量因为前一步失败空了,展开成了 `rm -rf /*`。好在当时是在测试机,而且 `rm` 的保护拦住了一部分,但那一刻我后背是凉的。从那以后,我写 shell 都会先把「别再出这种事」放第一位。

## 第一件事:打开严格模式

脚本开头加这一行,能挡掉大部分低级错误:

```bash
set -euo pipefail
```

拆开看:

- `-e`:任何命令失败就退出,不会带着错误继续跑
- `-u`:用到未定义变量就报错,专治上面那种空变量事故
- `-o pipefail`:管道里任何一个命令失败,整条管道算失败

很多人只加 `-e`,但管道默认只看最后一个命令,`cmd1 | cmd2` 里 cmd1 挂了不一定暴露。加上 pipefail 才完整。

## 第二件事:变量永远加引号

```bash
# 可能出问题
cp $SRC $DST

# 安全
cp "$SRC" "$DST"
```

变量里有空格或者为空时,不加引号会被拆成多个参数,甚至变成空参数。路径尤其容易踩。

数组也一样:

```bash
for f in "${files[@]}"; do
    echo "$f"
done
```

## 第三件事:删除前先确认

```bash
: "${TARGET_DIR:?TARGET_DIR is required}"

if [ ! -d "$TARGET_DIR" ]; then
    echo "target not found: $TARGET_DIR" >&2
    exit 1
fi

rm -rf "${TARGET_DIR:?}/"*
```

`${VAR:?message}` 的意思是变量为空就报错退出,这是防止空变量最直接的办法。再配一个目录存在性检查,双重保险。

## 第四件事:用 trap 清理临时资源

脚本中途失败,临时文件、锁、后台进程要收拾干净:

```bash
TMP_DIR=$(mktemp -d)
cleanup() {
    rm -rf "$TMP_DIR"
}
trap cleanup EXIT
```

不管正常结束还是出错,EXIT 都会触发。比在每个分支里手写清理靠谱。

## 第五件事:写日志,别全靠 echo

```bash
log() {
    echo "[$(date '+%F %T')] $*" >&2
}

log "start sync"
```

统一格式、带时间戳、输出到 stderr。排查问题的时候,时间线比散落的 echo 有用得多。

## 第六件事:检查依赖命令

脚本依赖的工具不存在,跑一半才报错很尴尬:

```bash
for cmd in curl jq tar; do
    command -v "$cmd" >/dev/null 2>&1 || {
        echo "missing: $cmd" >&2
        exit 1
    }
done
```

开头检查一遍,比中途失败好定位。

## 一个相对完整的骨架

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

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
log() { echo "[$(date '+%F %T')] $*" >&2; }

main() {
    : "${TARGET:?TARGET is required}"
    log "target=$TARGET"
    # 实际逻辑
}

main "$@"
```

用函数包起来,变量作用域清楚,也方便测试单个函数。

## 几个容易忽略的点

- `cd` 失败要处理,别假设一定成功
- 循环里读文件用 `while IFS= read -r line`,别用 `for line in $(cat)`
- 数值比较用 `(( ))`,字符串比较用 `[[ ]]`
- 尽量用 `#!/usr/bin/env bash`,别写死 `/bin/bash`

## 小结

Shell 脚本写起来快,但它的容错全靠你自己补。严格模式、变量加引号、删除前确认、trap 清理,这四条能挡掉绝大多数事故。剩下的就是日志和依赖检查,让排查更快。

脚本是给人省事的,别让它变成事故源头。花十分钟加这些防护,比出事后再改划算得多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。