好的,这是一个非常核心且实用的 Shell 知识点。${} 和 $() 都用于“替换”,但它们替换的内容和解决的问题完全不同。简单来说:
![图片[1]_Shell脚本变量${}与命令$()替换区别小结_知途无界](https://zhituwujie.com/wp-content/uploads/2025/11/d2b5ca33bd20251130103800.png)
- **
${}是变量扩展**,用于处理和引用变量。 - **
$()是命令替换**,用于将命令的执行结果赋值给变量或嵌入到其他命令中。
下面进行详细的小结和对比。
1. ${} – 变量扩展
${} 的核心是告诉 Shell:“请把括号里的变量名替换成它的值”。它的主要用途是精确界定变量的边界和对变量进行各种操作。
a. 基本变量引用(解决歧义)
当变量名后面紧跟其他字符时,直接使用 $variable 会产生歧义,${} 可以清晰地划定变量的范围。
示例:
filename="myfile"
echo $filenametxt # 错误!Shell 会试图寻找名为 "filenametxt" 的变量,结果为空。
echo ${filename}txt # 正确!输出 "myfiletxt"
b. 字符串操作(强大功能)
这是 ${} 最常用和最强大的地方,可以对变量值进行截取、替换、长度计算等。
| 语法 | 含义 | 示例 (str="Hello World") |
|---|---|---|
${var} | 基本引用 | ${str} → Hello World |
${#var} | 获取字符串长度 | ${#str} → 11 |
${var:position} | 从 position 开始截取子串 | ${str:6} → World |
${var:position:length} | 从 position 开始截取长度为 length 的子串 | ${str:0:5} → Hello |
${var#pattern} | 从开头删除最短匹配 pattern 的部分 | ${str#*o} → Wo World (删掉 Hell) |
${var##pattern} | 从开头删除最长匹配 pattern 的部分 | ${str##*o} → rld (删掉 Hello Wo) |
${var%pattern} | 从末尾删除最短匹配 pattern 的部分 | ${str%o*} → Hello W (删掉 rld) |
${var%%pattern} | 从末尾删除最长匹配 pattern 的部分 | ${str%%o*} → Hell (删掉 o World) |
${var/pattern/replacement} | 替换第一个匹配的 pattern | ${str/l/L} → HeLlo World |
${var//pattern/replacement} | 全局替换所有匹配的 pattern | ${str/l/L} → HeLLo WorLd |
${var:-default} | 如果 var 未设置或为空,则返回 default | ${undefined_var:-"empty"} → "empty" |
${var:=default} | 如果 var 未设置或为空,将其设为 default 并返回 | ${undefined_var:="now_set"} → "now_set" (且变量被赋值) |
${var:+alternate} | 如果 var 已设置且非空,则返回 alternate | ${str:+"is_set"} → is_set |
c. 数组操作
${} 也用于处理数组。
示例:
arr=(apple banana cherry)
echo ${arr[1]} # 访问第二个元素 → banana
echo ${arr[@]} # 获取所有元素 → apple banana cherry
echo ${#arr[@]} # 获取数组长度 → 3
echo ${arr[@]:1:2} # 从索引1开始取2个元素 → banana cherry
2. $() – 命令替换
$() 的核心是告诉 Shell:“请执行括号内的命令,然后把该命令的标准输出替换到这里”。
a. 基本用法
它将命令的输出捕获为一个字符串,可以赋值给变量或直接传递给其他命令。
示例:
# 赋值给变量
current_date=$(date +%Y-%m-%d)
echo "Today is $current_date" # 输出:Today is 2023-10-27
# 直接嵌入命令
echo "There are $(ls -l | wc -l) files in this directory."
b. 嵌套能力
与反引号 `command` 相比,$() 可以完美地进行嵌套,而不会引起解析混乱。
示例:
# 使用 $() 嵌套(清晰易读)
echo "The grandparent of my current directory is: $(basename $(dirname $(pwd)))"
# 使用反引号嵌套(难以阅读和维护)
echo "The grandparent of my current directory is: `basename \`dirname \\\`pwd\\\`\``"
现代脚本编写中,强烈推荐使用 $()。
c. 与反引号 `...` 的区别
- 可读性:
$()层次分明,易于嵌套和阅读。 - 转义:在
$()中,只需要转义$、\和反引号本身。在反引号中,反斜杠的行为更复杂,需要根据紧随其后的字符来决定是否转义。 - 兼容性:反引号在所有 POSIX Shell 中都有效,而
$()是 Bash 等现代 Shell 的特性(但已在绝大多数环境中普及)。为了最大兼容性(如/bin/sh),可能需要使用反引号,但在 Bash/Zsh 环境下无此顾虑。
总结与对比表格
| 特性 | ${} | $() |
|---|---|---|
| 名称 | 变量扩展 | 命令替换 |
| 作用 | 处理和引用变量 | 执行命令并获取其输出 |
| 核心问题 | 解决变量边界模糊、实现字符串/数组操作 | 将命令结果动态嵌入脚本 |
| 嵌套 | 主要用于变量名和模式,自身不常嵌套 | 支持完美嵌套,是其一大优势 |
| 类比 | 像一个变量处理器或文本编辑器,对变量内容进行加工 | 像一个函数调用,执行一段代码并拿到返回值 |
组合使用示例
它们经常结合使用,发挥强大威力:
#!/bin/bash
# 获取当前目录下所有 .log 文件,并构建一个带时间戳的新文件名
log_dir="./logs"
timestamp=$(date +%Y%m%d_%H%M%S)
latest_log=$(ls -t $log_dir/*.log | head -n1)
base_name=${latest_log##*/} # 从完整路径中提取文件名 "app.log"
name_no_ext=${base_name%.log} # 去掉 ".log" 后缀 → "app"
new_backup_name="${name_no_ext}_backup_${timestamp}.log"
echo "The latest log is: $latest_log"
echo "Its backup will be named: $new_backup_name"
# 执行备份命令
cp "$latest_log" "$log_dir/$new_backup_name"
在这个例子里:
$(date ...)和$(ls ...)是命令替换,用来获取动态数据。${latest_log##*/}和${base_name%.log}是变量扩展,用来处理字符串,提取和格式化文件名。
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END

























暂无评论内容