命令替换是一种强大的 shell 功能,它允许你执行一个命令,然后将其输出结果捕获,并插入到另一个命令的原始位置。

这就像把一个命令的执行结果“粘贴”到另一个命令的行里。
核心语法
命令替换有两种主要的语法,它们在现代 shell 中都可用,但反引号版本有一些缺点。
语法 1:反引号 (Backticks) - 传统方式
这是最古老的语法,但在所有 shell 中都可用。
command1 `command2`
command2:这部分将被 shell 执行。command2的输出结果:会替换掉command2的位置,command1再使用这个结果来执行。
示例: 假设你想要创建一个以当前日期和时间命名的日志文件。

# date 命令会输出类似 "Mon Oct 24 10:30:00 CST 2025" 的内容 touch `date +%Y-%m-%d`.log
执行过程:
- Shell 先执行
date +%Y-%m-%d。 - 假设输出是
2025-10-24。 - Shell 将原始命令行替换为
touch 2025-10-24.log。 - 然后执行这个新的命令,创建一个名为
2025-10-24.log的文件。
语法 2: - 现代、推荐的方式
这是目前最推荐、最安全、最强大的方式,几乎所有现代 shell(如 Bash, Zsh)都支持它。
command1 $(command2)
示例: 同样创建一个以日期命名的日志文件。
touch $(date +%Y-%m-%d).log
执行过程与反引号版本完全相同。

为什么要优先使用 而不是反引号?
尽管两种语法功能相似,但 在多个方面都优于反引号。
-
嵌套能力 可以无限嵌套,而反引号在嵌套时会变得非常混乱和难以阅读。
使用 的嵌套示例:
# 获取当前用户的主目录,并在其中创建一个文件夹 mkdir $(echo $(echo $HOME)/new_folder) # 等价于: mkdir /home/username/new_folder
使用反引号的嵌套示例:
# 这种写法非常不直观,容易出错 mkdir `echo \`echo $HOME\`/new_folder`
你需要用反斜杠
\来转义内层的反引号,可读性极差。 -
可读性 更清晰地表明“这是一个命令的执行结果”,而反引号很容易和单引号混淆,尤其是在某些字体中。
-
错误处理 如果反引号中的命令执行失败,整个命令替换可能会以难以预料的方式失败。 的错误处理机制通常更清晰。
实用示例
命令替换在日常系统管理中非常常见。
示例 1:遍历命令的输出
这是最常见的用法之一,例如遍历文件列表。
# 列出当前目录下的所有 .txt 文件,并逐个删除它们 for file in $(ls *.txt); do echo "Deleting file: $file" rm "$file" done
注意:当文件名中包含空格时,
for file in $(...)这种方式会出问题,因为它会按空格和换行符进行分割,更稳健的写法是使用find命令结合while循环,或者使用globstar选项 (shopt -s globstar)。
示例 2:将命令结果作为参数传递给另一个命令
# 获取当前系统上所有用户列表,并统计用户数量 echo "There are $(wc -l < /etc/passwd) users on this system."
< /etc/passwd是将文件内容作为标准输入传给wc -l。- 捕获了
wc -l的输出(一个数字)。 echo命令将这个数字插入到句子中。
示例 3:变量赋值
你可以将命令的输出赋值给一个变量。
# 获取当前工作目录 current_dir=$(pwd) # 获取系统的内核版本 kernel_version=$(uname -r) echo "You are in: $current_dir" echo "Your kernel version is: $kernel_version"
最佳实践:给变量赋值时,尽量使用 ,因为它更清晰。
示例 4:在条件判断中使用
# 检查 /etc/hosts 文件是否存在,如果存在则显示其行数 if [ -f /etc/hosts ]; then echo "The /etc/hosts file has $(wc -l < /etc/hosts) lines." else echo "The /etc/hosts file does not exist." fi
进阶用法:readarray 或 mapfile (处理多行输出)
当命令的输出包含多行内容,并且你希望将每一行作为一个独立的元素存入数组时,readarray (或其别名 mapfile) 是一个非常强大的工具。
场景: 你想获取所有 .sh 文件名,并对它们进行操作。
# mapfile 是 readarray 的一个更易读的别名
mapfile -t sh_files < <(find . -maxdepth 1 -name "*.sh")
# -t 选项会移除每行末尾的换行符
# < <(...) 是 "进程替换",它将 find 命令的输出像一个文件一样传递给 mapfile
echo "Found ${#sh_files[@]} shell scripts:"
# 遍历数组并打印每个文件名
for script in "${sh_files[@]}"; do
echo " - $script"
done
这种方式比 for file in $(find ...) 要健壮得多,因为它能正确处理文件名中的空格和特殊字符。
| 特性 | 反引号 ` |
|
|---|---|---|
| 语法 | `command` | $(command) |
|
| 嵌套 | 困难,需要转义 (```) | 容易,清晰 |
| 可读性 | 较差,易与单引号混淆 | 优秀,语义清晰 |
| 现代性 | 传统,兼容旧 shell | 现代,推荐用法 |
| 推荐度 | ⭐ (仅在需要兼容极旧环境时使用) | ⭐⭐⭐⭐⭐ (强烈推荐) |
核心要点:
- 命令替换就是执行一个命令,用其输出替换掉命令本身。
- 优先使用 语法,它更安全、更强大、更易读。
- 主要用途包括:变量赋值、参数传递、循环遍历。
- 对于处理多行输出并存储为数组的情况,请使用
mapfile或readarray。
