5.1.4 命令替换与退出状态

命令替换用于把命令输出作为字符串插入到其他命令或变量中,常见形式为反引号`cmd`与推荐的$(cmd)。后者可读性更好、支持嵌套。命令替换会去除末尾换行,结果若包含空格或通配符,未加引号会发生再次分词与路径扩展。

文章图片

命令替换示例与命令解释#

# 1) 获取日期并拼接文件名
now=$(date +%F)           # date +%F 输出 YYYY-MM-DD
echo "backup-$now.tar"

# 2) 统计错误行数并输出
count=$(grep -c "error" /var/log/syslog)  # -c 统计匹配行数
echo "error_count=$count"

# 3) 结果含空格时必须加引号
info="$(uname -a)"        # uname -a 输出含空格
echo "$info"

# 4) 嵌套命令替换(仅 $( ) 支持)
last_user="$(last | head -n 1 | awk '{print $1}')"
echo "last_login_user=$last_user"

常见坑演示:

# 目录中如果有空格文件名,未加引号会被拆分成多个参数
files=$(ls /tmp/testdir)  # 可能发生分词
echo $files               # 风险:空格被拆分

# 正确做法:用双引号包裹
files="$(ls /tmp/testdir)"
echo "$files"

退出状态与判定逻辑#

退出状态(exit status)范围 0~255,其中 0 表示成功、非 0 表示失败或特定错误。上一条命令的状态通过 $? 获取,推荐直接判断命令返回,避免 $? 被覆盖。

# 错误示例:$? 可能被后续命令覆盖
cp /tmp/a /tmp/b
echo "copy done"
if [ $? -ne 0 ]; then
  echo "copy failed"
fi

# 推荐写法:直接判断命令
if cp /tmp/a /tmp/b; then
  echo "copy ok"
else
  echo "copy failed"
fi

# 显式返回状态码
if grep -q "nginx" /etc/services; then
  exit 0
else
  exit 2
fi

组合示例:检测服务与失败退出#

#!/usr/bin/env bash
# /opt/scripts/check_nginx.sh
# 功能:检测 nginx 进程并在失败时退出非 0

pid="$(pgrep -f nginx)"
if [ -z "$pid" ]; then
  echo "nginx not running"
  exit 1
fi

# 再次确认端口是否监听
if ss -lnt | grep -q ":80"; then
  echo "nginx running, pid=$pid"
  exit 0
else
  echo "nginx pid exists but port not listening"
  exit 3
fi

执行与预期:

chmod +x /opt/scripts/check_nginx.sh
/opt/scripts/check_nginx.sh
echo "exit_code=$?"
# 预期:运行中 exit_code=0;否则为 1 或 3

错误处理与调试建议#

# 1) 关键命令失败即退出(注意与条件语句交互)
set -e
set -o pipefail   # 管道中任一命令失败都返回失败

# 2) 需要捕获失败时使用逻辑或
systemctl restart nginx || { echo "restart failed"; exit 1; }

# 3) 查看上一条命令的退出状态
some_cmd
echo "status=$?"

常见排错:

  • 命令替换结果包含空格导致参数错位:加双引号 var="$(cmd)"
  • set -egrep -q 无匹配会导致脚本退出:用 || true 或改为 if grep -q ...; then
  • $?echo 覆盖:将判断紧跟在命令后或使用 if cmd; then.

练习#

  1. 编写脚本输出 /var/log 下文件数量,并在数量为 0 时返回退出码 5。
  2. 写一个脚本:若 nginx 不在运行则启动,启动成功返回 0,失败返回 1。要求使用命令替换与退出状态判断。
  3. 模拟 grep -qset -e 下的失败退出,并给出两种修复方式。