定位:Bash 内置核心命令,用来配置 Shell 的执行行为、开关各种特性,是脚本调试、提升脚本健壮性的必备工具。 核心原则:减号
-开启选项,加号+关闭选项(新手最容易记反)
一、基础认知
1. 本质
set 是 Bash 内置命令,不是外部程序,用来修改 Shell 运行时的参数开关。 写在脚本开头会作用于整个脚本;也可以在终端临时执行,只作用于当前 Shell 会话。
2. 基础语法
# 开启某个特性
set -选项名
# 关闭某个特性
set +选项名
# 查看所有当前生效的设置
set -o
❗ 高频易错:
-是开启,+是关闭,和常规命令参数逻辑相反。
二、核心高频选项(运维脚本必掌握)
第一类:调试排错专用
1. set -x 开启执行跟踪(最常用调试工具)
- 作用:脚本执行每一行命令前,先打印这行命令的展开后内容(变量替换完成、通配符展开后的真实命令)。
- 价值:可以直观看到变量到底替换成了什么值、命令有没有拼接错误,是定位脚本诡异问题的神器。
- 对应场景:你之前遇到的变量拼写错误、括号不匹配、路径拼接错误,加
-x一眼就能定位。
示例
#!/bin/bash
set -x
port=9000
echo "检测端口 $port"
ss -tlnp | grep ":$port"
执行后会输出:
+ port=9000
+ echo '检测端口 9000'
检测端口 9000
+ ss -tlnp
+ grep :9000
带
+号的行就是set -x打印的真实执行命令。
- 关闭:
set +x - 等价用法:不用改脚本,直接调试执行:
bash -x script.sh
第二类:生产脚本健壮性三件套(强烈推荐所有运维脚本开头加上)
2. set -e 遇错即停
- 作用:脚本中任意一条命令返回非 0 退出码,脚本立即终止执行,不会继续往下跑。
- 价值:防止错误累积扩大。比如前面创建目录失败,后面还继续往目录里写文件,最终造成更难排查的故障。
坑点(非常重要): 默认对管道命令不生效。例如:
cmd1 | cmd2 | cmd3
管道的退出码默认取最后一个命令的退出码,只要 cmd3 成功,哪怕 cmd1、cmd2 失败了,set -e 也不会触发终止。
解决:配合
set -o pipefail一起使用。
3. set -u 未定义变量报错
- 作用:使用了没有定义的变量时,脚本直接报错退出,而不是把变量当成空字符串静默执行。
- 价值:捕获变量名拼写错误。比如你把
port写成prot,不加-u会当成空值继续跑,出现各种诡异结果;加了-u直接报错定位。
示例
#!/bin/bash
set -u
echo $prot # 变量名拼错,直接报错:prot: unbound variable
4. set -o pipefail 管道错误捕获
- 作用:管道中任意一个命令执行失败,整个管道的退出码就为失败。
- 价值:配合
set -e使用,让管道里的错误也能触发脚本终止,不会被掩盖。
✅ 生产脚本标准开头(严格模式):
```
!/bin/bash
set -euo pipefail
```这三行组合,能规避 80% 以上的脚本静默错误。
第三类:其他实用选项
5. set -n 语法检查
- 作用:只检查脚本语法,不实际执行任何命令。
- 场景:写完脚本先做语法预检,相当于编译检查。
bash -n script.sh # 等价于脚本里写 set -n
6. set -f 关闭通配符展开
- 作用:禁用
*?等通配符的自动展开,当成普通字符串处理。 - 场景:处理包含通配符的文本、避免路径意外展开。
三、易错点汇总(错题笔记)
- ❌ 记反开关:认为
+开启、-关闭 ✅ 纠正:-开启特性,+关闭特性。 - ❌ 以为
set -e能捕获所有错误 ✅ 纠正:默认对管道无效,必须搭配set -o pipefail才能完整覆盖。 - ❌
set -u会影响所有变量 ✅ 纠正:$#``$@``$?等特殊内置变量不会触发未定义报错。 - ❌ 以为 set 是外部命令 ✅ 纠正:Bash 内置命令,用
help set查看帮助,没有man set。 - ❌ 脚本中间改了 set 不恢复 ✅ 纠正:临时关闭特性后,用完记得重新开启,比如:
set +u
# 这里执行允许空变量的逻辑
set -u
四、结合你的巡检脚本示例
以你之前的端口监控脚本为例,加上严格模式和调试开关:
#!/bin/bash
set -euo pipefail
# 调试时解开下面注释
# set -x
log(){
echo "[$(date '+%H:%M:%S')] [$1] $2"
}
check_port(){
local port="$1"
if ss -tlnp | grep -q ":$port ";then
log "run" "$port 正在监听"
return 0
else
log "error" "$port 未监听"
return 1
fi
}
ports=(9000 9001 9002)
for p in "${ports[@]}"
do
check_port "$p"
done
五、速记口诀
减开加关别记反, x 调试看展开, e 遇错就停下来, u 防变量没定义, pipefail 管道错误逮, 生产开头三件套,euo pipefail 少不了。
评论