目录
Please enable Javascript to view the contents

bash脚本set命令说明

 ·  ☕ 4 分钟
核心结论说明
set -e 不是万能错误处理它有很多例外场景,不能替代显式判断
set -u 处理的是未定义变量,不是空变量变量已定义但值为空不会报错
管道必须配合 set -o pipefail否则管道返回值默认取最后一个命令
set -x 适合调试,不适合长期打开可能打印敏感变量、Token、密码
常见组合是 set -Eeuo pipefail但要理解副作用,不建议无脑复制

1.简介

set 是 Bash 内建命令,用于修改当前 Shell 的运行参数。

脚本中常见写法:

1
set -euo pipefail

或:

1
set -Eeuo pipefail

这些参数可以提升脚本失败时的可见性,但也会改变脚本控制流。生产脚本中建议使用,但必须理解它们的边界。

2.说明

2.1 常用参数总览

参数长写法作用
set -eset -o errexit命令返回非 0 时退出脚本
set -uset -o nounset使用未定义变量时报错
set -xset -o xtrace执行命令前打印展开后的命令
set -o pipefail-管道中任一命令失败,则整个管道失败
set -Eset -o errtraceERR trap 继承到函数、子 Shell、命令替换

查看当前 Shell 参数:

1
set -o

临时关闭某个参数:

1
2
3
4
set +e
set +u
set +x
set +o pipefail

2.2 set -u:未定义变量直接报错

set -u 只针对未定义变量,不针对空字符串。

1
2
3
set -u

echo "$name"

输出:

1
bash: name: unbound variable

空变量不会报错:

1
2
3
set -u
name=""
echo "$name"

处理可选变量时,建议使用默认值写法:

1
name=${name:-default}

区别:

写法含义
${var:-default}变量未定义或为空时使用默认值
${var-default}变量未定义时使用默认值,变量为空则仍为空
${var:?message}变量未定义或为空时报错并退出

必填参数示例:

1
image=${IMAGE:?IMAGE is required}

2.3 set -x:打印执行过程

set -x 会在命令执行前打印展开后的命令,常用于排查脚本执行路径。

1
2
3
4
set -x
name=hex
echo "hello $name"
set +x

输出类似:

1
2
3
+ name=hex
+ echo 'hello hex'
hello hex

可以修改 PS4 增加行号:

1
2
PS4='+ ${BASH_SOURCE}:${LINENO}: '
set -x

需要注意:set -x 会打印变量展开后的值,不适合在含有密码、Token、AK/SK 的脚本中长期打开。

2.4 set -e:命令失败立即退出

默认情况下,Bash 中某条命令失败后,脚本会继续执行。

1
2
false
echo "still running"

开启 set -e 后:

1
2
3
set -e
false
echo "never run"

false 返回非 0,脚本退出。

手动处理错误:

1
command || { echo "command failed"; exit 1; }

允许某条命令失败:

1
command || true

或临时关闭:

1
2
3
4
5
6
7
8
set +e
command
rc=$?
set -e

if [ "$rc" -ne 0 ]; then
  echo "command failed: $rc"
fi
set -e 只能兜底,不能替代关键步骤的显式错误处理。关键命令建议保留 if|| { ... }

2.5 set -e 的常见例外

set -e 有很多不会退出的场景,最容易误判。

2.5.1 if / while 条件判断

1
2
3
4
5
6
7
set -e

if grep -q "hello" file.txt; then
  echo "found"
fi

echo "continue"

grep 未匹配时返回 1,但脚本不会退出,因为它处于 if 条件判断位置。

2.5.2 && / || 命令链

1
2
3
4
set -e

false && echo ok
echo "continue"

false 不会导致退出,因为它位于 && 命令链中。

2.5.3 取反 !

1
2
3
4
set -e

! false
echo "continue"

这种写法不会退出。

2.5.4 管道默认只看最后一个命令

1
2
3
4
set -e

false | true
echo "continue"

默认情况下,管道整体返回值取最后一个命令 true,所以脚本继续执行。

2.6 set -o pipefail:管道中任一命令失败则失败

开启 pipefail 后:

1
2
3
4
5
set -e
set -o pipefail

false | true
echo "never run"

管道中 false 失败,整个管道返回失败,脚本退出。

常见场景:

1
curl -fsSL https://example.com/install.sh | bash

如果不启用 pipefailcurl 失败但 bash 可能仍返回 0,脚本会误判成功。

更稳妥的写法:

1
2
3
4
set -euo pipefail

curl -fsSL https://example.com/install.sh -o /tmp/install.sh
bash /tmp/install.sh

2.7 set -E 与 trap ERR

trap ERR 可以在命令失败时统一处理错误。

1
2
3
4
5
6
7
8
9
set -Eeuo pipefail

trap 'echo "error at line $LINENO"' ERR

fail() {
  false
}

fail

如果没有 set -EERR trap 在函数、子 Shell、命令替换中的继承行为会受限。

常见模板:

1
2
set -Eeuo pipefail
trap 'echo "failed at line $LINENO: $BASH_COMMAND"' ERR

注意:$BASH_COMMAND 可能包含敏感参数,生产日志中需要谨慎打印。

2.8 推荐模板

普通脚本:

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

需要统一错误日志:

1
2
3
#!/usr/bin/env bash
set -Eeuo pipefail
trap 'echo "failed at line $LINENO"' ERR

调试脚本:

1
2
#!/usr/bin/env bash
set -Eeuxo pipefail

不建议长期使用 set -x 跑生产脚本,尤其脚本中包含:

  • 数据库密码;
  • API Token;
  • SSH 私钥路径和参数;
  • 云厂商 AK/SK。

2.9 常见坑

问题原因建议
set -u 后可选变量报错变量未定义使用 ${var:-default}
set -e 没有退出命令在 if/while/&&/`
管道前半段失败但脚本继续未启用 pipefail使用 set -o pipefail
日志泄露密钥开启 set -x敏感逻辑前 set +x
`commandtrue` 掩盖真实错误

3.总结

  1. set -u 处理未定义变量,不处理空变量;
  2. set -e 可以让脚本在错误时尽快失败,但存在很多例外;
  3. 管道场景必须配合 set -o pipefail,否则默认只看最后一个命令;
  4. set -x 适合调试,但可能泄露敏感信息;
  5. 常见模板是 set -Eeuo pipefail,但关键命令仍建议显式错误处理。

4.参考

分享

Hex
作者
Hex
CloudNative Developer