Skip to content

03|命令替换与退出码

上一篇用变量存了固定的值。但运维脚本里很多值是动态的——当前日期、磁盘使用率、某进程的 PID。这些值得先跑命令拿到,再存进变量。

把命令的输出存进变量,靠命令替换。判断命令成功没,靠退出码。本篇讲这两个。

一、命令替换

命令替换有两种写法:

bash
# 推荐写法
today=$(date +%F)

# 老写法(反引号)
today=`date +%F`

两种效果一样,把 date +%F 的输出赋给 today。推荐用 $()——嵌套时清晰,反引号嵌套要转义很难读。

看实际用法:

bash
today=$(date +%F)
backup_dir="/data/backup/${today}"
echo "备份到: $backup_dir"
# 备份到: /data/backup/2026-06-23

再比如拿到磁盘使用率做判断:

bash
usage=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%')
echo "根分区使用率: ${usage}%"

命令替换里可以套管道,把一串处理结果存进变量。

二、命令替换里的引号

命令替换 $() 在双引号里会展开,在单引号里不展开:

bash
echo "今天是 $(date +%F)"     # 双引号:展开
echo '今天是 $(date +%F)'      # 单引号:原样输出
text
今天是 2026-06-23
今天是 $(date +%F)

要展开就用双引号。

三、退出码

每条命令执行完,Shell 都记一个退出码(exit code):0 表示成功,非 0 表示失败。退出码存在 $? 里:

bash
ls /etc/passwd
echo $?       # 0,成功

ls /not-exist
echo $?       # 2,失败

不同的非 0 值代表不同失败原因,但日常用 0 和非 0 两种就够:0 成功,非 0 失败。

判断上一条命令成功没:

bash
mkdir /tmp/backup
if [ $? -eq 0 ]; then
    echo "创建成功"
else
    echo "创建失败"
    exit 1
fi

$? 要紧跟在命令后取——中间只要再跑一条命令(哪怕 echo),$? 就被覆盖了:

bash
mkdir /tmp/backup
echo "正在处理"     # 这条命令会覆盖 $?
if [ $? -eq 0 ]; then   # 这里的 $? 是 echo 的退出码,不是 mkdir 的

更简洁的写法是直接用命令做条件,不存中间变量:

bash
if mkdir /tmp/backup; then
    echo "创建成功"
fi

四、&& 和 ||:逻辑运算符

&&|| 用退出码做短路逻辑,比 if 简洁:

bash
mkdir /tmp/backup && echo "创建成功"
mkdir /tmp/backup || echo "创建失败"
  • 命令A && 命令B:A 成功才执行 B
  • 命令A || 命令B:A 失败才执行 B

常见的"成功就继续、失败就退出"模式:

bash
mkdir /tmp/backup || { echo "创建失败"; exit 1; }

注意 || 后面跟一组命令要加大括号 { },且大括号和命令间要有空格,最后要有分号。

另一个常见用法——给可能失败的命令一个默认值:

bash
# grep 没找到返回 1,用 || 让脚本不退出
result=$(grep "error" /var/log/messages) || result="没有错误记录"

五、管道的退出码

管道默认只看最后一个命令的退出码:

bash
grep "error" /var/log/messages | head
echo $?    # head 成功返回 0,即使 grep 没找到

第 2 篇讲过,set -o pipefail 能让管道任一段失败都算失败。但要注意:grep 没匹配到返回 1 是预期行为,加了 pipefail 会让脚本退出。这种情况要 || true

bash
set -euo pipefail
grep "error" /var/log/messages | head || true

六、自定义退出码

脚本自己可以用 exit 返回退出码,让调用方判断:

bash
#!/usr/bin/env bash
set -euo pipefail

if [ ! -d /data ]; then
    echo "错误: /data 不存在" >&2
    exit 2      # 自定义失败码
fi

echo "检查通过"
exit 0           # 显式成功退出

约定俗成的退出码:0 成功,1 一般失败,2 用法错误。具体数字不重要,重要的是脚本失败时返回非 0,让 &&||、调用方、cron 能判断。

错误信息用 >&2 输出到 stderr,和正常输出分开:

bash
echo "错误: /data 不存在" >&2    # 进 stderr
echo "正常信息"                  # 进 stdout

这样调用方可以 2>/dev/null 只看正常输出,或 2>error.log 单独存错误。

学会了之后

命令替换和退出码让脚本能动态取值、能判断成败、能根据成败决定下一步。$(date) 拿日期、$(pidof nginx) 拿 PID、$? 判断成败、&& || 做逻辑——这些是脚本从"一串命令"变成"有判断的程序"的基础。

下一篇讲位置参数——脚本怎么接收外部传进来的参数。