Linux Shell 变量扩展进阶:深入理解 ${} 特殊用法
Linux Shell 变量扩展进阶:深入理解 ${} 特殊用法
- Linux Shell 变量扩展进阶:深入理解 ${} 特殊用法
- 一、变量默认值与条件判断
- 1. `${varname:-word}`:变量不存在或为空时使用默认值
- 2. `${varname:+word}`:变量存在且非空时使用替代值
- 3. `${varname:=word}`:变量不存在或为空时赋值并返回
- 4. `${varname:?word}`:变量不存在或为空时报错
- 总结:默认值操作对比
- 二、模式匹配与字符串截取
- 1. `${varname#pattern}`:从开头删除最短匹配
- 2. `${varname##pattern}`:从开头删除最长匹配
- 3. `${varname%pattern}`:从结尾删除最短匹配
- 4. `${varname%%pattern}`:从结尾删除最长匹配
- 模式匹配规则
- 总结:模式匹配截取对比
- 三、其他实用的 ${} 用法
- 1. 字符串长度:${#varname}
- 2. 子串提取:${varname:position:length}
- 四、实际应用案例
- 案例1:处理文件扩展名
- 案例2:安全处理命令行参数
- 案例3:解析路径信息
- 总结
Linux Shell 变量扩展进阶:深入理解 ${} 特殊用法
在 Linux Shell 脚本中,变量扩展不仅仅是简单的 $var
替换。${}
语法提供了丰富的变量处理功能,包括默认值设置、字符串截取、条件判断等。掌握这些高级用法可以让你的脚本更简洁、更健壮。本文将详细介绍 ${}
的常用特殊用法,包括默认值操作和模式匹配截取。
一、变量默认值与条件判断
当处理变量时,我们经常需要应对变量未定义、为空或非空的情况。Shell 提供了四种常用的默认值处理语法:
1. ${varname:-word}
:变量不存在或为空时使用默认值
作用:如果 varname
未定义或为空(值为空字符串),则返回 word
;否则返回变量本身的值。
示例:
# 未定义变量
echo ${undefined_var:-"默认值"} # 输出:默认值# 空变量
empty_var=""
echo ${empty_var:-"默认值"} # 输出:默认值# 已定义且非空的变量
name="Linux"
echo ${name:-"默认值"} # 输出:Linux
应用场景:为可能未设置的变量提供默认值,避免脚本出错。
2. ${varname:+word}
:变量存在且非空时使用替代值
作用:如果 varname
已定义且非空,则返回 word
;否则返回空值。
示例:
# 未定义变量
echo "结果: ${undefined_var:+替代值}" # 输出:结果: # 空变量
empty_var=""
echo "结果: ${empty_var:+替代值}" # 输出:结果: # 已定义且非空的变量
name="Linux"
echo "结果: ${name:+替代值}" # 输出:结果: 替代值
应用场景:仅当变量有有效值时执行特定操作,如:
# 如果配置文件存在,则加载它
config_file="app.conf"
[ -f ${config_file:+$config_file} ] && source $config_file
3. ${varname:=word}
:变量不存在或为空时赋值并返回
作用:如果 varname
未定义或为空,则将 word
赋值给 varname
并返回 word
;否则返回变量本身的值。
示例:
# 未定义变量
echo ${undef_var:="新值"} # 输出:新值
echo $undef_var # 输出:新值(变量已被赋值)# 空变量
empty_var=""
echo ${empty_var:="新值"} # 输出:新值
echo $empty_var # 输出:新值(变量已被更新)# 已定义且非空的变量
name="Linux"
echo ${name:="新值"} # 输出:Linux(变量值不变)
应用场景:初始化变量,确保后续操作中有可用的值:
# 确保日志目录存在,不存在则设置默认值
${LOG_DIR:="/var/log/myapp"}
mkdir -p $LOG_DIR # 现在可以安全使用该变量
4. ${varname:?word}
:变量不存在或为空时报错
作用:如果 varname
未定义或为空,则将 word
作为错误信息输出到标准错误并终止脚本;否则返回变量本身的值。
示例:
# 未定义变量
echo ${undef_var:?"变量未定义"} # 输出:bash: undef_var: 变量未定义(脚本终止)# 空变量
empty_var=""
echo ${empty_var:?"变量为空"} # 输出:bash: empty_var: 变量为空(脚本终止)# 已定义且非空的变量
name="Linux"
echo ${name:?"错误信息"} # 输出:Linux(正常执行)
应用场景:验证脚本必需的参数或变量,确保它们已正确设置:
# 脚本必须提供一个文件名参数
filename=$1
${filename:?"请提供文件名作为参数"} # 如果未提供参数,脚本将报错退出
总结:默认值操作对比
语法 | 变量未定义 | 变量为空 | 变量非空 | 副作用 |
---|---|---|---|---|
${var:-word} | 返回 word | 返回 word | 返回 var | 无 |
${var:+word} | 返回空 | 返回空 | 返回 word | 无 |
${var:=word} | 返回 word 并赋值 | 返回 word 并赋值 | 返回 var | 修改变量 |
${var:?word} | 报错退出 | 报错退出 | 返回 var | 可能终止脚本 |
二、模式匹配与字符串截取
${}
还提供了基于模式匹配的字符串截取功能,通过 #
、##
、%
、%%
符号实现从开头或结尾删除匹配的子串。
1. ${varname#pattern}
:从开头删除最短匹配
作用:从变量值的开头开始,删除与 pattern
匹配的最短子串,返回剩余部分。
示例:
path="/usr/local/bin/python"# 删除从开头到第一个 / 的部分
echo ${path#/} # 输出:usr/local/bin/python# 删除从开头到第一个 . 的部分(没有匹配则返回原值)
echo ${path#*.} # 输出:/usr/local/bin/python# 删除协议部分(如 http:// 或 https://)
url="https://example.com/path"
echo ${url#*://} # 输出:example.com/path
2. ${varname##pattern}
:从开头删除最长匹配
作用:从变量值的开头开始,删除与 pattern
匹配的最长子串,返回剩余部分。
示例:
path="/usr/local/bin/python"# 删除从开头到最后一个 / 的部分(获取文件名)
echo ${path##*/} # 输出:python# 获取文件路径中的文件名(与上面效果相同)
file="/home/user/docs/report.pdf"
echo ${file##*/} # 输出:report.pdf# 从路径中获取目录部分
echo ${path##/usr} # 输出:/local/bin/python
3. ${varname%pattern}
:从结尾删除最短匹配
作用:从变量值的结尾开始,删除与 pattern
匹配的最短子串,返回剩余部分。
示例:
file="document.txt.bak"# 删除最后一个 . 及其后面的部分
echo ${file%.*} # 输出:document.txt# 从路径中删除文件名,保留目录
path="/usr/local/bin/python"
echo ${path%/*} # 输出:/usr/local/bin# 处理没有扩展名的文件
simple_file="readme"
echo ${simple_file%.*} # 输出:readme(无变化)
4. ${varname%%pattern}
:从结尾删除最长匹配
作用:从变量值的结尾开始,删除与 pattern
匹配的最长子串,返回剩余部分。
示例:
file="document.txt.bak"# 删除所有 . 及其后面的部分
echo ${file%%.*} # 输出:document# 处理多层目录
path="/a/b/c/d/file.txt"
echo ${path%%/*} # 输出:(空字符串,因为匹配了从第一个 / 到结尾的所有内容)# 更实用的例子:获取不带任何扩展名的文件名
archive="data.tar.gz"
echo ${archive%%.*} # 输出:data
模式匹配规则
上述用法中的 pattern
支持通配符:
*
:匹配任意长度的任意字符?
:匹配单个任意字符[abc]
:匹配 a、b 或 c 中的任意一个[!abc]
:匹配除 a、b、c 之外的任意字符
示例:
text="abc123def456"# 从开头删除到第一个数字
echo ${text#[!0-9]*} # 输出:123def456# 从结尾删除最后一个数字及之后的内容
echo ${text%[0-9]*} # 输出:abc123def4
总结:模式匹配截取对比
语法 | 作用 | 匹配方向 | 匹配长度 | 典型用途 |
---|---|---|---|---|
${var#pattern} | 从开头删除匹配部分 | 从左到右 | 最短匹配 | 移除前缀 |
${var##pattern} | 从开头删除匹配部分 | 从左到右 | 最长匹配 | 获取文件名 |
${var%pattern} | 从结尾删除匹配部分 | 从右到左 | 最短匹配 | 移除后缀 |
${var%%pattern} | 从结尾删除匹配部分 | 从右到左 | 最长匹配 | 移除所有扩展名 |
三、其他实用的 ${} 用法
除了上述两大类,${}
还有一些常用功能:
1. 字符串长度:${#varname}
作用:返回变量值的长度(字符数)。
示例:
name="Linux"
echo ${#name} # 输出:5text="Hello World"
echo ${#text} # 输出:11(包含空格)
2. 子串提取:${varname:position:length}
作用:从 position
开始(0 为起始位置),提取长度为 length
的子串。length
可选,不指定则提取到结尾。
示例:
str="abcdefgh"echo ${str:2} # 输出:cdefgh(从位置2开始)
echo ${str:2:3} # 输出:cde(从位置2开始,提取3个字符)
echo ${str: -3} # 输出:fgh(负数表示从结尾开始计数)
四、实际应用案例
结合上述功能,我们可以实现一些实用的脚本功能:
案例1:处理文件扩展名
filename="report.pdf.bak"# 获取文件名(无扩展名)
echo ${filename%%.*} # 输出:report# 获取扩展名(最后一个)
echo ${filename##*.} # 输出:bak# 获取第一个扩展名
echo ${filename#*.} # 输出:pdf.bak
案例2:安全处理命令行参数
# 脚本名:process.sh
# 用法:./process.sh <输入文件> [输出目录]# 检查必需参数
input_file=$1
${input_file:?"请提供输入文件作为第一个参数"}# 设置输出目录默认值
output_dir=${2:-"./output"}# 确保输出目录存在
mkdir -p $output_direcho "处理文件: $input_file"
echo "输出目录: $output_dir"
案例3:解析路径信息
full_path="/usr/local/share/doc/readme.txt"# 获取目录部分
dir_path=${full_path%/*}
echo "目录: $dir_path" # 输出:目录: /usr/local/share/doc# 获取文件名(含扩展名)
file_name=${full_path##*/}
echo "文件名: $file_name" # 输出:文件名: readme.txt# 获取文件名(不含扩展名)
base_name=${file_name%.*}
echo "基础名称: $base_name" # 输出:基础名称: readme
总结
${}
语法为 Shell 变量处理提供了强大的功能,掌握这些用法可以:
- 更优雅地处理变量默认值和边界情况
- 无需调用外部命令(如
sed
、awk
)即可完成字符串处理 - 使脚本更简洁、高效且易于维护
重点记住:
:-
、:+
、:=
、:?
用于变量默认值和条件判断#
、##
、%
、%%
用于基于模式的字符串截取- 这些操作都是 Shell 内置功能,比调用外部命令性能更好
若有转载,请标明出处:https://blog.csdn.net/CharlesYuangc/article/details/152365841