Shell脚本变量${}与命令$()替换区别小结

好的,这是一个非常核心且实用的 Shell 知识点。${}$() 都用于“替换”,但它们替换的内容和解决的问题完全不同。简单来说:

图片[1]_Shell脚本变量${}与命令$()替换区别小结_知途无界
  • ​**${} 是变量扩展**,用于处理和引用变量
  • ​**$() 是命令替换**,用于将命令的执行结果赋值给变量或嵌入到其他命令中。

下面进行详细的小结和对比。


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
喜欢就点个赞,支持一下吧!
点赞12 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容