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 -e下grep -q无匹配会导致脚本退出:用|| true或改为if grep -q ...; then。$?被echo覆盖:将判断紧跟在命令后或使用if cmd; then.
练习#
- 编写脚本输出
/var/log下文件数量,并在数量为 0 时返回退出码 5。 - 写一个脚本:若
nginx不在运行则启动,启动成功返回 0,失败返回 1。要求使用命令替换与退出状态判断。 - 模拟
grep -q在set -e下的失败退出,并给出两种修复方式。