set 命令选项

set 命令用于设置 Shell 的运行选项,可以改变脚本的行为。合理使用 set 选项,能让脚本更安全、更健壮,也更容易调试。

常用选项

set -e

遇到错误立即退出。默认情况下,脚本会继续执行,即使某个命令失败了。

#!/bin/bash
set -e

mkdir /tmp/test
ls /notexist
echo "这行不会执行"

ls /notexist 失败后脚本立即退出。

set -u

使用未定义变量时报错。默认情况下,未定义变量会被当作空字符串。

#!/bin/bash
set -u

echo "姓名:$name"

运行结果:

script.sh: line 4: name: unbound variable

set -o pipefail

管道中任意命令失败,整个管道返回失败。默认只返回最后一个命令的状态。

#!/bin/bash
set -o pipefail

false | true
echo $?

运行结果:

1

如果没有 pipefail,$? 会是 0。

set -x

开启调试模式,打印执行的每条命令。

#!/bin/bash
set -x

name="张三"
echo "你好,$name"

组合使用

推荐的组合:

#!/bin/bash
set -euo pipefail

这是编写健壮脚本的标准配置:

  • -e:错误退出
  • -u:未定义变量报错
  • -o pipefail:管道错误传播

其他常用选项

set -f

禁用文件名扩展(通配符):

set -f
echo *

输出 * 而不是文件列表。

set -n

只读取脚本不执行,用于语法检查:

set -n

set -v

显示读取的输入行:

set -v

set -C

禁止重定向覆盖已存在的文件:

set -C
echo "test" > file.txt  # 如果 file.txt 存在会报错
echo "test" >| file.txt  # 强制覆盖

set -h

缓存命令位置,加速命令查找:

set -h

查看当前选项

set -o
set +o

set -o 以可读格式显示,set +o 以可重入格式显示。

临时禁用选项

+ 代替 - 可以关闭选项:

#!/bin/bash
set -e

# 遇到错误退出
cmd1

# 临时禁用错误退出
set +e
cmd_might_fail
set -e

# 恢复错误退出
cmd2

选项的简写

简写完整形式
-e-o errexit
-u-o nounset
-x-o xtrace
-v-o verbose
-f-o noglob
-n-o noexec
-C-o noclobber

实用示例

健壮的脚本模板

#!/bin/bash
set -euo pipefail

readonly SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
readonly SCRIPT_NAME=$(basename "${BASH_SOURCE[0]}")

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
}

error() {
    log "错误:$*" >&2
    exit 1
}

cleanup() {
    log "清理资源..."
    # 清理代码
}

trap cleanup EXIT

main() {
    log "开始执行"
    # 主逻辑
    log "执行完成"
}

main "$@"

处理可选变量

set -u 会让未定义变量报错,可以用默认值语法处理:

#!/bin/bash
set -u

name=${1:-"匿名"}
port=${PORT:-8080}
debug=${DEBUG:-false}

忽略特定命令的错误

#!/bin/bash
set -e

# 这个命令可能失败,但不影响脚本继续执行
rm -f /tmp/temp || true

# 这个命令必须成功
important_command

管道中的错误处理

#!/bin/bash
set -eo pipefail

# 如果 grep 没有匹配到内容,会返回非零状态
# 使用 || true 避免脚本退出
result=$(grep "pattern" file.txt || true)

# 或者检查结果
if ! grep -q "pattern" file.txt; then
    echo "未找到匹配"
fi

注意事项

set -e 的局限

  • 不会捕获 &&|| 中的错误
  • 不会捕获管道中非最后一个命令的错误(需要 pipefail)
  • 在函数中的行为可能不符合预期
#!/bin/bash
set -e

# 这个错误不会被捕获
false && echo "不会执行"

# 这个也不会
true || false

# 需要这样写
if ! false; then
    echo "命令失败"
    exit 1
fi

小结

  • set -e 遇错退出,set -u 未定义变量报错
  • set -o pipefail 管道错误传播
  • set -euo pipefail 是推荐的组合
  • set +e 临时禁用选项
  • 配合默认值语法处理可选变量
  • 了解 set -e 的局限,合理使用