5.2.7 退出码与错误处理基础

退出码与错误处理基础#

退出码(Exit Status)用于表示命令执行结果,0 成功,非 0 失败。脚本通过 $? 获取上一条命令的退出状态。正确使用退出码能让自动化流程可预测、可恢复。

原理草图:命令链路与退出码流转#

文章图片

1) 获取与返回退出码(含命令解释)#

# 获取上一条命令退出码:$? 只记录“上一条”命令的状态
ls /tmp
echo $?   # 0 表示 ls 成功

# 自定义脚本退出码:exit N
if [[ ! -f /etc/passwd ]]; then
  echo "file not found" >&2   # 输出到 stderr
  exit 2                      # 自定义错误码
fi
exit 0

命令解释$? 读取上一条命令的退出码;exit N 终止脚本并返回 N。

2) 常见退出码约定(运维常见)#

  • 0:成功
  • 1:一般性错误(权限不足/语法错误)
  • 2:参数使用不当
  • 126:命令不可执行
  • 127:命令未找到
  • 128+n:被信号终止(如 130=Ctrl+C)

3) 基本错误处理模式(完整可执行示例)#

#!/bin/bash
# /opt/scripts/backup.sh
set -euo pipefail

SRC="/data/a"
DST="/backup/a"

# 依赖检查
if ! command -v rsync >/dev/null 2>&1; then
  echo "error: rsync not found" >&2
  exit 127
fi

# 关键操作 + 立即判断
rsync -a "$SRC" "$DST" || { echo "error: rsync failed" >&2; exit 1; }

echo "backup ok"
exit 0

命令解释
- set -euo pipefail:遇错退出/未定义变量报错/管道任一失败即失败
- command -v rsync:检查命令是否存在
- rsync -a:归档模式(保持权限/时间)

预期效果:依赖存在且同步成功则输出 backup ok,失败则脚本以非 0 退出。

4) 标准错误输出与日志分离#

# 正常输出到 stdout,错误输出到 stderr
echo "task ok"
echo "error: invalid input" >&2

# 将错误日志单独收集
/opt/scripts/backup.sh 1>/var/log/backup.out 2>/var/log/backup.err

命令解释1> 重定向标准输出,2> 重定向标准错误。

5) 与信号相关的退出码(练习用)#

# 运行并手动 Ctrl+C 终止,观察退出码
sleep 100
echo $?

预期效果:按 Ctrl+C 后退出码为 130

6) 小型场景:服务重启失败即退出#

#!/bin/bash
# /opt/scripts/restart_nginx.sh
set -euo pipefail

if systemctl restart nginx; then
  echo "nginx restarted"
else
  echo "restart failed" >&2
  exit 1
fi

命令解释systemctl restart 成功返回 0,失败返回非 0

7) 排错清单(退出码相关)#

  • 127:命令未找到
  • 排查:command -v <cmd>、检查 PATH
  • 126:命令不可执行
  • 排查:ls -l <cmd>chmod +x <cmd>
  • 1/2:参数或权限问题
  • 排查:<cmd> --helpidls -l
  • 管道失败未捕获
  • 排查:是否启用 set -o pipefail

8) 练习(动手操作)#

  1. 练习1:自定义退出码
    写脚本判断 /etc/hosts 是否存在,存在则 exit 0,不存在 exit 3,并输出到 stderr
  2. 练习2:依赖检查
    写脚本检查 curljq 是否存在,缺任一退出 127
  3. 练习3:管道错误捕获
    对比 set -o pipefail 前后:
    bash grep "root" /not_exists | wc -l echo $?