2026年01月22日/ 浏览 41
在 Shell 脚本中,set 命令是一个非常强大且常用的内置命令。它主要用于设置或取消 Shell 的运行选项(参数),以及操作位置参数。
为了让你更直观地掌握,我将这些参数分为 “脚本安全卫士”、“调试追踪员” 和 “功能扩展器” 三类,并结合详细的代码示例进行讲解。
这三个参数通常写在脚本的头部(#!/bin/bash 之后),用于让脚本在遇到错误时更“健壮”。
用途:只要任何一条命令返回非 0 状态(表示出错),脚本立即终止执行。这类似于其他语言中的“抛出异常”。
示例:不加 -e 的情况(默认行为):#!/bin/bash echo "第一步:开始执行" foo_command_that_does_not_exist # 这条命令会报错,但脚本继续 echo "第二步:清理工作" # 即使上面出错了,这行依然会执行,可能导致数据不一致输出:第一步:开始执行 line 3: foo_command_that_does_not_exist: command not found 第二步:清理工作 # <--- 不希望看到的后续执行加了 set -e 的情况:#!/bin/bash set -e echo "第一步:开始执行" foo_command_that_does_not_exist # 脚本在这里遇到错误,直接退出 echo "第二步:清理工作" # 这行永远不会被执行输出:第一步:开始执行 line 4: foo_command_that_does_not_exist: command not found # 脚本直接退出,不会打印“第二步”用途:当尝试使用一个未定义的变量时,立即报错并退出。这能防止因拼写错误导致的逻辑漏洞。
示例:#!/bin/bash set -u name="Alice" echo "你好, $nmae" # 注意:变量名拼写错误了 (nmae 而不是 name) 输出:line 4: nmae: unbound variable # 直接报错,而不是打印空字符串用途:配合 -e 使用。默认情况下,管道(|)的返回值是最后一个命令的返回值。如果前面的命令失败但最后一个成功,-e 不会生效。pipefail 让只要管道中任意一个命令失败,整个管道就算失败。
示例:#!/bin/bash set -e # 场景:foo命令不存在,但输出通过cat显示了,脚本没报错就结束了(这很危险) foo | cat echo "看,即使foo不存在,我也执行了!" 加上 set -o pipefail 修复:#!/bin/bash set -eo pipefail # 现在如果 foo 失败,整个管道就算失败,配合 -e 脚本会立即退出 foo | cat当你需要排查脚本为什么没按预期运行时,这两个参数是神器。
用途:在执行每一行命令之前,先打印出实际执行的命令(变量会被替换成真实值)。这对于查看变量拼接是否正确非常有用。
示例:#!/bin/bash set -x user="Bob" count=5 for i in $(seq $count); do echo "处理用户 $user 的第 $i 项" done 输出(注意行首的 + 号是 -x 打印出来的):+ user=Bob + count=5 + seq 5 + for i in $(seq $count) + echo 处理用户 Bob 的第 1 项 处理用户 Bob 的第 1 项 + for i in $(seq $count) + echo 处理用户 Bob 的第 2 项 处理用户 Bob 的第 2 项 ...用途:显示 Shell 读取的输入行。与 -x 不同,它显示的是脚本原本的样子,而不是展开后的样子。
用途:set 后面直接跟参数,会将这些参数赋值给位置参数 $1, $2, ...。这在解析命令行参数或模拟参数时非常有用。
示例:#!/bin/bash # 假设脚本运行时没传参数,但我们想模拟传入了 "apple" 和 "banana" set -- "apple" "banana" "cherry" echo "第一个参数是: $1" # 输出: apple echo "第二个参数是: $2" # 输出: banana echo "参数个数是: $#" # 输出: 3用途:此后在脚本中定义的所有变量,都会自动被 export 到环境变量中,供后续执行的子进程使用。
示例:#!/bin/bash set -a DB_HOST="127.0.0.1" # 这个变量自动变成了环境变量 DB_PORT="3306" # 这个也是 set +a # 关闭自动导出 # 启动一个子脚本,它能直接读取到 DB_HOST 和 DB_PORT ./backup_script.sh参数
别名
用途描述
-e
errexit
命令返回非0值时,立即退出脚本。
-u
nounset
使用未定义变量时报错。
-x
xtrace
打印执行的每一条命令(带变量值)。
-v
verbose
打印读取的每一行输入(原始脚本行)。
-C
noclobber
防止重定向 > 覆盖已存在的文件。
-H
histexpand
启用 ! 开头的历史命令展开(如 !!)。
-P
physical
执行命令时,使用物理路径(不跟随符号链接)。
在生产环境的 Shell 脚本头部,我强烈建议你加上这行“黄金组合”:
#!/bin/bash set -u name="Alice" echo "你好, $nmae" # 注意:变量名拼写错误了 (nmae 而不是 name) 输出:line 4: nmae: unbound variable # 直接报错,而不是打印空字符串这行代码的意思是:遇到错误退出、遇到未定义变量报错、管道命令也要严格检查错误。这能帮你避免绝大多数低级错误。