核心 · Key Idea
一句话:bash 脚本写起来快、坑也多。set -euo pipefail + 引号习惯 + shellcheck 三件套,能避掉 90% 的隐藏 bug。
是什么#
最小可用脚本:
#!/usr/bin/env bash
set -euo pipefail
NAME="${1:-world}"
echo "Hello, $NAME"
for f in *.log; do
echo "处理 $f"
gzip "$f"
done#!/usr/bin/env bash 比 #!/bin/bash 兼容性更好(用 PATH 找)。
打个比方#
打个比方 · Analogy
bash 像胶水语言:把各种小工具粘在一起。别用它写复杂逻辑 —— 上 100 行就该考虑 Python / Go。
关键概念#
shebang#!
脚本第一行告诉内核用什么解释器。
set -eerrexit
一旦某条命令返回非 0 就立刻退出。
set -unounset
用未定义变量直接报错(避免 rm -rf $UNDEFINED/)。
set -o pipefailpipefail
管道任意一段失败整条都失败(默认只看最后一段)。
trap信号 / 退出钩子
trap cleanup EXIT —— 脚本退出前清临时文件。
$1 / $@ / $#位置参数
$1 第一个参数;$@ 全部;$# 个数。
$(cmd)命令替换
把 cmd 输出嵌进字符串(比反引号清晰)。
常用模板#
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
log() { echo "[$(date +%T)] $*" >&2; }
cleanup() {
rm -f "$tmp"
}
trap cleanup EXIT
tmp=$(mktemp)
log "开始"
# 防止变量为空时炸:
: "${API_KEY:?需要 API_KEY}"
# 安全引号:路径含空格也不会炸
for f in "$dir"/*.log; do
[[ -e "$f" ]] || continue
gzip "$f"
done
log "完成"调试套路#
shellcheck 是必装:能查出未引号、误用 ==、[ ] vs [[ ]] 之类一堆坑。
实操要点#
- 永远引号包变量:
"$var"—— 含空格 / 通配符的字符串不会变成多个参数。 [[ ]]优于[ ]:现代 bash 测试条件,不需要引号也安全。- 善用
${var:-default}/${var:?msg}:参数兜底 / 必填校验。 - 不要用 ls 解析文件名:用
for f in *或find ... -print0 | xargs -0。 - 错误别忽略:
cmd || die "msg"、if ! cmd; then ... fi。 - 临时文件用 mktemp:避免 race condition 和 shared /tmp 冲突。
- 复杂逻辑请改 Python:bash > 100 行通常意味着选错语言。
易混点#
bash 脚本
放到文件、加 shebang、`set -euo pipefail`。
适合复用、CI 中跑。
适合复用、CI 中跑。
ad-hoc 命令
一次性命令链,`cmd1 | cmd2 | cmd3`。
临时排查可以,**不要复制粘贴当生产脚本**。
临时排查可以,**不要复制粘贴当生产脚本**。