Bash 变量内容操作完全指南
前言
Bash 提供了极为丰富的变量内容操作功能,涵盖了删除、替换、替代、默认值处理、大小写转换、子串提取等各个方面。这些操作统称为 参数扩展(Parameter Expansion),是 Bash 脚本编程中最常用也最强大的特性之一。
本文将从零开始,系统性地整理所有与变量内容相关的操作,并区分哪些是基础必会、哪些是进阶内容、哪些属于高级特性。
第一部分:删除操作
删除操作用于从变量内容中移除匹配特定模式的字符串。
1.1 前缀删除(从开头匹配)
| 语法 | 名称 | 匹配方式 | 示例 |
|---|---|---|---|
${var#pattern} | 最小匹配 | 删除开头最短匹配 | var="abc123abc" → ${var#a*c} → 123abc |
${var##pattern} | 最大匹配 | 删除开头最长匹配 | var="abc123abc" → ${var##a*c} → (空) |
典型用途:
- 提取文件名:
${path##*/}(删除最后一个斜杠之前的所有内容) - 去除前缀:
${string##*:}(删除最后一个冒号之前的所有内容)
1.2 后缀删除(从结尾匹配)
| 语法 | 名称 | 匹配方式 | 示例 |
|---|---|---|---|
${var%pattern} | 最小匹配 | 删除结尾最短匹配 | var="abc123abc" → ${var%a*c} → abc123 |
${var%%pattern} | 最大匹配 | 删除结尾最长匹配 | var="abc123abc" → ${var%%a*c} → (空) |
典型用途:
- 去除文件扩展名:
${filename%.*}(删除最后一个点及之后) - 去除后缀:
${string%% *}(删除第一个空格及之后)
1.3 删除操作的模式说明
所有 pattern 都使用 通配符(globbing) 语法,而非正则表达式:
*:匹配任意字符串(包括空串)?:匹配任意单个字符[...]:匹配方括号内的任一字符,如[a-z]、[0-9][^...]:匹配不在方括号内的任一字符
第二部分:替代操作
替代操作用新字符串替换匹配到的内容。
2.1 基础替代
| 语法 | 名称 | 行为 | 示例 |
|---|---|---|---|
${var/old/new} | 单次替换 | 替换第一个匹配 | var="abc123abc" → ${var/abc/XYZ} → XYZ123abc |
${var//old/new} | 全局替换 | 替换所有匹配 | var="abc123abc" → ${var//abc/XYZ} → XYZ123XYZ |
2.2 锚定替代
| 语法 | 名称 | 行为 | 示例 |
|---|---|---|---|
${var/#old/new} | 开头替换 | 仅当匹配开头时替换 | var="abc123abc" → ${var/#abc/XYZ} → XYZ123abc |
${var/%old/new} | 结尾替换 | 仅当匹配结尾时替换 | var="abc123abc" → ${var/%abc/XYZ} → abc123XYZ |
2.3 删除形式的替代(替换为空)
省略 new 部分,相当于删除匹配内容:
| 语法 | 行为 | 示例 |
|---|---|---|
${var/pattern} | 删除第一个匹配 | var="abc123abc" → ${var/abc} → 123abc |
${var//pattern} | 删除所有匹配 | var="abc123abc" → ${var//abc} → 123 |
${var/#pattern} | 仅开头匹配时删除 | var="abc123abc" → ${var/#abc} → 123abc |
${var/%pattern} | 仅结尾匹配时删除 | var="abc123abc" → ${var/%abc} → abc123 |
第三部分:默认值替换(回退赋值)
这部分用于处理变量未定义或为空的情况。每个操作都有带冒号和不带冒号两个版本,区别在于是否检查空字符串。
3.1 替换(不修改变量本身)
| 语法 | 行为 | 说明 |
|---|---|---|
${var-expr} | 未定义时替换 | 仅当 var 未定义时,用 expr 代替 |
${var:-expr} | 未定义或空时替换 | 当 var 未定义或为空时,用 expr 代替 |
区别示例:
unset var # var 未定义
echo ${var-default} # 输出 default
echo ${var:-default} # 输出 default
var="" # var 已定义但为空
echo ${var-default} # 输出(空)
echo ${var:-default} # 输出 default
var="hello"
echo ${var-default} # 输出 hello
echo ${var:-default} # 输出 hello
3.2 赋值(修改变量本身)
| 语法 | 行为 | 说明 |
|---|---|---|
${var=expr} | 未定义时赋值 | 仅当 var 未定义时,将 expr 赋值给 var |
${var:=expr} | 未定义或空时赋值 | 当 var 未定义或为空时,将 expr 赋值给 var |
3.3 报错退出
| 语法 | 行为 | 说明 |
|---|---|---|
${var?expr} | 未定义时报错 | 仅当 var 未定义时,输出 expr 错误并退出 |
${var:?expr} | 未定义或空时报错 | 当 var 未定义或为空时,输出 expr 错误并退出 |
3.4 条件替换(检测是否已定义)
| 语法 | 行为 | 说明 |
|---|---|---|
${var+expr} | 已定义时替换 | 仅当 var 已定义时,用 expr 代替(否则为空) |
${var:+expr} | 非空时替换 | 仅当 var 非空时,用 expr 代替(否则为空) |
典型用途:检测变量是否被显式设置(包括空字符串)
if [ -n "${1+set}" ]; then
echo "用户传入了第一个参数(可能为空字符串)"
fi
第四部分:大小写转换
Bash 4.0 引入,用于改变字母的大小写形态。
| 语法 | 行为 | 示例 |
|---|---|---|
${var^} | 首字母转大写 | var="hello world" → ${var^} → Hello world |
${var^^} | 全部转大写 | var="hello world" → ${var^^} → HELLO WORLD |
${var,} | 首字母转小写 | var="HELLO" → ${var,} → hELLO |
${var,,} | 全部转小写 | var="HELLO" → ${var,,} → hello |
${var~} | 首字母大小写反转 | var="Hello" → ${var~} → hello |
${var~~} | 全部大小写反转 | var="Hello World" → ${var~~} → hELLO wORLD |
高级用法:模式限定
这些操作符可以接受一个可选的模式参数,只转换匹配的部分:
var="hello world"
echo ${var^^[aeiou]} # 只转元音:hEllO wOrld
echo ${var^[aeiou]} # 只转匹配的第一个元音:hEllo world?实际行为需测试
第五部分:变换操作
Bash 4.4 引入的高级特性,通过 @ 操作符实现。
| 语法 | 行为 | 示例 |
|---|---|---|
${var@U} | 转大写 | ${var@U} 等同于 ${var^^} |
${var@L} | 转小写 | ${var@L} 等同于 ${var,,} |
${var@Q} | 转义为可重用的引用格式 | var="hello 'world'" → ${var@Q} → 'hello '\''world'\''' |
${var@E} | 展开转义序列 | var="hello\nworld" → ${var@E} → 实际换行 |
${var@P} | 按提示符规则展开 | 支持 \u(用户名)、\h(主机名)等 |
${var@A} | 显示赋值语句 | 输出 declare -- var="value" 格式 |
第六部分:子串提取
从变量中截取部分内容。
| 语法 | 行为 | 示例 |
|---|---|---|
${var:offset} | 从 offset 取到末尾 | var="hello.txt" → ${var:2} → llo.txt |
${var:offset:length} | 从 offset 取 length 个 | var="hello.txt" → ${var:2:3} → llo |
${var: -n} | 取末尾 n 个字符 | var="hello.txt" → ${var: -4} → .txt |
注意事项:
offset从 0 开始计数- 取末尾字符时,
:后面必须有空格,以避免与${var:-default}语法混淆
第七部分:间接扩展
通过变量名引用另一个变量的值。
| 语法 | 行为 | 示例 |
|---|---|---|
${!var} | 间接引用 | ref="name"、name="world" → ${!ref} → world |
典型用途:
- 动态变量名处理
- 配置系统(当变量名由运行时决定)
第八部分:模式匹配增强(extglob)
通过 shopt -s extglob 开启扩展模式匹配,获得更强大的模式语法:
| 模式 | 含义 | 示例 |
|---|---|---|
?(pattern) | 匹配 0 或 1 次 | abc?(def) 匹配 abc 或 abcdef |
*(pattern) | 匹配 0 或多次 | abc*(def) 匹配 abc、abcdef、abcdefdef… |
+(pattern) | 匹配 1 或多次 | abc+(def) 匹配 abcdef、abcdefdef… |
@(pattern1|pattern2) | 匹配其中之一 | abc@(def|xyz) 匹配 abcdef 或 abcxyz |
!(pattern) | 匹配非该模式 | abc!(def) 匹配除 abcdef 外的任意内容 |
开启方式:
shopt -s extglob
var="abc123def"
echo ${var//+(digit)/} # 删除连续数字(假设定义了 digit 模式)
完整速查表
| 分类 | 操作符 | 一句话说明 |
|---|---|---|
| 删除(前缀) | # | 从开头删除最短匹配 |
## | 从开头删除最长匹配 | |
| 删除(后缀) | % | 从结尾删除最短匹配 |
%% | 从结尾删除最长匹配 | |
| 替代 | / | 替换第一个匹配 |
// | 替换所有匹配 | |
/# | 仅开头匹配时替换 | |
/% | 仅结尾匹配时替换 | |
| 默认值(不赋值) | - | 未定义时替换 |
:- | 未定义或空时替换 | |
| 默认值(赋值) | = | 未定义时赋值 |
:= | 未定义或空时赋值 | |
| 报错 | ? | 未定义时报错 |
:? | 未定义或空时报错 | |
| 条件替换 | + | 已定义时替换 |
:+ | 非空时替换 | |
| 大小写 | ^ | 首字母大写 |
^^ | 全部大写 | |
, | 首字母小写 | |
,, | 全部小写 | |
~ | 首字母大小写反转 | |
~~ | 全部大小写反转 | |
| 变换 | @U | 大写转换(Bash 4.4+) |
@L | 小写转换(Bash 4.4+) | |
@Q | 转义引用(Bash 4.4+) | |
@E | 展开转义序列(Bash 4.4+) | |
@P | 提示符展开(Bash 4.4+) | |
@A | 显示赋值语句(Bash 4.4+) | |
| 子串 | :offset | 截取到末尾 |
:offset:len | 截取指定长度 | |
: -n | 取末尾 n 个 | |
| 间接 | !var | 间接引用 |
难易程度分级
基础必会(日常使用频率极高)
${var#pattern}、${var%pattern}:去路径、去扩展名${var/old/new}:简单的字符串替换${var:-default}:设置默认值${var:offset}、${var:offset:length}:截取子串
进阶掌握(提升脚本效率)
${var##pattern}、${var%%pattern}:最长匹配删除${var//old/new}:全局替换${var^}、${var^^}、${var,}、${var,,}:大小写转换${var: -n}:取末尾字符${var=default}、${var:?error}:赋值和错误处理
高级特性(特定场景使用)
${!var}:间接引用${var@U}、${var@L}、${var@Q}等变换操作extglob扩展模式匹配${var+expr}、${var:+expr}条件替换的精妙用法
总结
Bash 的变量内容操作看似繁多,但大多数都有清晰的命名逻辑:
#和%让人联想到行首(#在键盘左侧)和行尾(%在右侧)——虽然是民间记忆法- 单个字符表示最小匹配,双字符表示最大匹配
- 冒号
:的加入表示同时检查空字符串 - 大小写转换用
^(上箭头表示大写)、,(下箭头?表示小写)、~(取反)
掌握这些操作,你将能够用极其简洁的代码完成原本需要调用 sed、awk、cut、tr 等外部命令才能实现的字符串处理任务。这不仅能提升脚本执行效率(避免创建子进程),也能让代码更加清晰易读。
最重要的建议:不需要死记硬背所有操作符,先熟练掌握 #、%、/、:- 这四个最常用的,然后在使用过程中逐步学习和扩展。这份文档可以作为随时查阅的参考手册。