写bash脚本的时候,很多人只关心“怎么让它运行”,却忽略了“出错了怎么办”。尤其是在做端口映射这类操作时,一个命令失败可能导致服务无法访问,而脚本却还在继续跑,结果就是问题被掩盖,排查起来头大。
别让脚本“装死”
比如你写了个脚本自动配置iptables做端口转发,中间一条命令拼错了,或者权限没给够,命令执行失败了。默认情况下,bash不会因为一条命令失败就停下来,它会继续往下走。这就像烧菜时盐撒多了,你不尝出来,还继续上桌,客人吃了才发现问题。
解决办法是让脚本在出错时主动“刹车”。最简单的一招是在脚本开头加上:
set -e
这一行的意思是:只要有任何一条命令返回非零状态(也就是失败),脚本立刻退出。这样能避免错误蔓延。
配合使用 set -u 和 set -o pipefail
set -e 虽好,但有盲点。比如引用了一个未定义的变量,它不一定能捕获到。这时候可以加上:
set -u
它会让脚本在使用未定义变量时报错退出。另外,管道命令比如 cmd1 | cmd2,即使前面的cmd1失败,整体也可能返回成功。要解决这个问题,加上:
set -o pipefail
这三个经常一起用,放在脚本头部很常见:
#!/bin/bash
set -euo pipefail
用 trap 捕获异常
有时候你希望脚本在退出前做点清理工作,比如删除临时文件、恢复原始端口配置。这就用得上 trap。
比如你在脚本里临时改了防火墙规则,万一中途出错,得把规则还原。可以这样写:
cleanup() {
echo "正在恢复端口配置..."
iptables -t nat -D PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 2>/dev/null || true
}
trap cleanup EXIT
这里的 trap 会在脚本退出(无论是正常还是出错)时执行 cleanup 函数。注意用了 || true,避免清理命令失败导致又触发一次错误。
手动判断关键步骤
不是所有地方都适合直接退出。比如尝试连接某个端口,连不上可能只是暂时的。这时候可以用 if 判断:
if ! nc -z localhost 8080; then
echo "端口8080不可访问,请检查服务是否启动" >&2
exit 1
fi
这样你可以自定义错误提示,也能控制脚本走向。
日志别省
出问题时,有没有日志差别很大。建议在关键步骤加一些输出,尤其是带时间戳的:
echo "[$(date +'%Y-%m-%d %H:%M:%S')] 正在配置端口映射..."
这些信息能帮你快速定位是哪一步出了问题,特别是在定时任务或远程服务器上跑脚本时。