shell之扩展
Shell脚本中的参数扩展(Parameter Expansion)是一项强大且灵活的功能,它允许你对变量的值进行操作、转换和计算。下面我将为你详细讲解常见的参数扩展用法。
先了解基础
参数扩展的基本形式是 ${parameter}
,用于获取变量 parameter
的值。花括号 {}
在多数情况下是可选的,但当变量名后紧接其他字符时,它们能明确界定变量名的范围。
1. 处理默认值和空值
这类扩展用于在变量未设置或为空时提供备选值或执行操作。
${parameter:-word}
:若parameter
未设置或为空,则扩展为word
;否则扩展为parameter
的值。parameter
自身的值不会改变。unset var; echo "Result: ${var:-default}" # 输出: Result: default var="custom"; echo "Result: ${var:-default}" # 输出: Result: custom echo "var is still: $var" # 输出: var is still: custom
${parameter:=word}
:若parameter
未设置或为空,则将其设置为word
并扩展为word
;否则扩展为parameter
的值。注意:不能用于位置参数和特殊参数。unset var; echo "Result: ${var:=default}" # 输出: Result: default echo "var is now: $var" # 输出: var is now: default var="custom"; echo "Result: ${var:=default}" # 输出: Result: custom
${parameter:?word}
:若parameter
未设置或为空,则将word
(或默认错误消息)输出到标准错误(stderr)并退出脚本;否则扩展为parameter
的值。常用于强制要求提供参数。unset var; : ${var:?Error: var is not set} # 输出到stderr: bash: var: Error: var is not set 并退出 var="ok"; echo "Result: ${var:?Error}" # 输出: Result: ok
${parameter:+word}
:若parameter
已设置且不为空,则扩展为word
;否则不进行任何替换(扩展为空)。这类似于一种“条件替换”。unset var; echo "Result: ${var:+replaced}" # 输出: Result: var="value"; echo "Result: ${var:+replaced}" # 输出: Result: replaced echo "var remains: $var" # 输出: var remains: value
2. 操作字符串:删除与替换
这类扩展用于对变量存储的字符串进行模式匹配和修改。
${parameter#pattern}
:从parameter
值的开头删除与pattern
匹配的最短部分。path="/usr/local/bin"; echo "${path#*/}" # 输出: usr/local/bin (删除最短的`*/`,即第一个`/`及之前)
${parameter##pattern}
:从parameter
值的开头删除与pattern
匹配的最长部分。path="/usr/local/bin"; echo "${path##*/}" # 输出: bin (删除最长的`*/`,即最后一个`/`及之前的所有内容)
${parameter%pattern}
:从parameter
值的末尾删除与pattern
匹配的最短部分。file="data.txt.backup"; echo "${file%.*}" # 输出: data.txt (删除最短的`.*`,即最后一个`.`及之后)
${parameter%%pattern}
:从parameter
值的末尾删除与pattern
匹配的最长部分。file="data.txt.backup"; echo "${file%%.*}" # 输出: data (删除最长的`.*`,即第一个`.`及之后的所有内容)
${parameter/pattern/string}
:将parameter
值中第一个匹配pattern
的子字符串替换为string
。str="hello world hello"; echo "${str/hello/hi}" # 输出: hi world hello
${parameter//pattern/string}
:将parameter
值中所有匹配pattern
的子字符串替换为string
。str="hello world hello"; echo "${str//hello/hi}" # 输出: hi world hi
${parameter/#pattern/string}
:如果pattern
匹配parameter
值的开头,则将其替换为string
。str="hello world"; echo "${str/#hello/hi}" # 输出: hi world str="world hello"; echo "${str/#hello/hi}" # 输出: world hello (开头不匹配)
${parameter/%pattern/string}
:如果pattern
匹配parameter
值的末尾,则将其替换为string
。str="file.txt"; echo "${str/%.txt/.doc}" # 输出: file.doc str="file.txt.backup"; echo "${str/%.txt/.doc}" # 输出: file.txt.backup (末尾不是精确匹配.txt)
3. 获取字符串长度和子字符串
${#parameter}
:扩展为parameter
值的字符长度。str="hello"; echo "Length: ${#str}" # 输出: Length: 5
${parameter:offset}
:从parameter
值的第offset
个字符开始(0-based)扩展,直到末尾。str="0123456789"; echo "${str:3}" # 输出: 3456789
${parameter:offset:length}
:从parameter
值的第offset
个字符开始(0-based),扩展指定长度的字符。str="0123456789"; echo "${str:3:4}" # 输出: 3456
注意:
offset
可为负数,表示从字符串末尾向前计算。负offset
前必须有一个空格,以避免与:-
混淆。str="0123456789"; echo "${str: -4:2}" # 输出: 67 (从倒数第4个字符开始,取2个)
4. 变量转换与间接引用
${parameter@operator}
:对parameter
的值应用特定的operator
。Q
: 将值转义为可被 Shell 重用的引用形式。E
: 对值中的反斜杠转义序列进行扩展(在某些版本的 Bash 中)。
var="hello 'world'"; echo "${var@Q}" # 输出: 'hello '\''world'\'''
${!prefix*}
或${!prefix@}
:扩展为所有以prefix
开头的变量名,由IFS
的第一个字符分隔。echo "${!BASH*}" # 可能输出: BASH BASHOPTS BASHPID ... (所有以BASH开头的变量名)
${!name}
:间接引用。如果name
是一个变量,且它的值是另一个变量的名称,则${!name}
扩展为那个变量的值。foo="bar"; bar="actual value"; echo "${!foo}" # 输出: actual value
5. 修改大小写 (Bash 4.0+)
${parameter^}
:将parameter
值的第一个字符转换为大写。
${parameter^^}
:将parameter
值的所有字符转换为大写。
${parameter,}
:将parameter
值的第一个字符转换为小写。
${parameter,,}
:将parameter
值的所有字符转换为小写。word="hello"; echo "${word^}" # 输出: Hello echo "${word^^}" # 输出: HELLO WORD="HELLO"; echo "${WORD,}" # 输出: hELLO echo "${WORD,,}" # 输出: hello
注意:
^
和,
在某些上下文中也可以用~
替代。
✨ 实用技巧与注意事项
保护变量名:当变量名后紧跟字母、数字或下划线时,使用
{}
是必须的,否则 Bash 会将其视为一个整体变量名。var="hello"; echo "${var}world" # 输出: helloworld echo "$varworld" # 输出: (空,因为变量`varworld`未设置)
模式匹配:在
#
,%
,##
,%%
,/
,//
,#
,%
中,pattern
遵循 Shell 的通配符模式(glob pattern),而不是正则表达式。*
匹配任意字符,?
匹配单个字符。空值与未设置:对于
${parameter:-word}
,${parameter:=word}
,${parameter:?word}
,${parameter:+word}
,冒号:
的存在至关重要。有冒号:检查
parameter
是 未设置 还是 为空。无冒号:仅检查
parameter
是否 未设置(即使设置为空字符串,也算已设置)。
数组处理:许多参数扩展形式也可用于数组。例如
${#array[@]}
获取数组元素个数,${array[@]:offset:length}
获取子数组。引号的重要性:为了处理包含空格的字符串或文件名,总是将参数扩展用双引号引起来是一种好习惯。
file="file with spaces.txt" rm $file # 可能出错,会被视为三个参数 rm "$file" # 正确,是一个参数
性能考虑:在可能的情况下,使用 Shell 的内置字符串操作(参数扩展)通常比调用外部命令(如
sed
,awk
,cut
)更高效。
掌握这些参数扩展技巧能极大提升你编写 Shell 脚本的效率和脚本的灵活性。