在 Bash 中,存储值的实体被称为参数。它们的值可以是带有常规语法的字符串或数组,或者当使用 declare
内置函数设置特殊属性时,它们可以是整数或关联数组。 有三种类型的参数:位置参数、特殊参数和变量。
为了简洁起见,本文重点介绍一些可用于字符串变量的扩展方法,尽管这些方法同样适用于其他类型的参数。
变量赋值和未经修改的扩展
在分配变量时,其名称必须仅由字母数字和下划线字符组成,并且不能以数字开头。 等号周围不能有空格; 名称必须紧靠其前面,值必须紧随其后
$ variable_1="my content"
将值存储在变量中,只有当我们稍后回忆起该值时才有用;在 Bash 中,用其值替换参数引用称为扩展。 要扩展参数,只需在名称前加上 $
字符,可以选择将名称括在花括号中
$ echo $variable_1 ${variable_1}
my content my content
至关重要的是,如上例所示,扩展发生在调用命令之前,因此命令永远不会看到变量名,只会看到作为参数传递给它的文本,该文本是由扩展产生的。 此外,参数扩展发生在词语分割 *之前*; 如果扩展的结果包含空格,则应引用扩展以保持参数的完整性(如果需要)
$ printf "%s\n" ${variable_1}
my
content
$ printf "%s\n" "${variable_1}"
my content
参数扩展修饰符
参数扩展远不止简单的插值。 在参数扩展的花括号内,某些运算符及其参数可以放置在名称之后、右括号之前。 这些运算符可以调用条件、子集、子字符串、替换、间接、前缀列表、元素计数和大小写修改扩展方法,从而修改扩展的结果。 除了重新赋值运算符(=
和 :=
)之外,这些运算符仅影响参数的扩展,而不会修改参数的值以供后续扩展使用。
条件参数扩展
条件参数扩展允许根据参数是未设置、为空还是有内容进行分支。 基于这些条件,参数可以扩展为其值、默认值或备用值; 抛出一个可定制的错误; 或将参数重新分配给默认值。 下表显示了条件参数扩展 - 每行显示使用运算符可能修改扩展的参数扩展,列显示给定参数状态(如列标题所示)时该扩展的结果。 带有 :
前缀的运算符将具有空值的参数视为未设置。
参数扩展 | 未设置 var | var="" | var="gnu" |
${var-default} | default | — | gnu |
${var:-default} | default | default | gnu |
${var+alternate} | — | alternate | alternate |
${var:+alternate} | — | — | alternate |
${var?error} | error | — | gnu |
${var:?error} | error | error | gnu |
表中的 =
和 :=
运算符的功能分别与 -
和 :-
相同,不同之处在于 =
变体将变量重新绑定到扩展的结果。
举个例子,让我们尝试在 OUT_FILE 变量指定的文件上打开用户的编辑器。 如果未指定 EDITOR 环境变量或我们的 OUT_FILE 变量,我们将遇到问题。 使用条件扩展,我们可以确保在扩展 EDITOR 变量时,我们获得指定的值,或者至少获得一个合理的默认值
$ echo ${EDITOR}
/usr/bin/vi
$ echo ${EDITOR:-$(which nano)}
/usr/bin/vi
$ unset EDITOR
$ echo ${EDITOR:-$(which nano)}
/usr/bin/nano
在上述基础上,如果未指定文件名,我们可以在运行时运行编辑器命令并中止并显示有用的错误
$ ${EDITOR:-$(which nano)} ${OUT_FILE:?Missing filename}
bash: OUT_FILE: Missing filename
子字符串参数扩展
参数可以扩展到其内容的一部分,可以通过偏移量,也可以通过删除与模式匹配的内容来实现。 指定子字符串偏移量时,可以选择指定长度。 如果运行 Bash 版本 4.2 或更高版本,则可以使用负数作为字符串结尾的偏移量。 请注意负偏移量周围使用的括号,这确保 Bash 不会将扩展解析为具有上述条件默认扩展运算符
$ location="CA 90095"
$ echo "Zip Code: ${location:3}"
Zip Code: 90095
$ echo "Zip Code: ${location:(-5)}"
Zip Code: 90095
$ echo "State: ${location:0:2}"
State: CA
获取子字符串的另一种方法是从字符串中删除与模式匹配的字符,可以从左边缘使用 # 和 ## 运算符,或者从右边缘使用 % 和 %% 运算符。 一个有用的助记符是 # 出现在注释的左侧,% 出现在数字的右侧。 当运算符加倍时,它会贪婪地匹配,而不是单个版本,单个版本会删除与模式匹配的最少字符集。
var="open source" | ||||
---|---|---|---|---|
参数扩展 | 偏移量为 5 长度为 4 |
|||
${var:offset} | source | |||
${var:offset:length} | sour | |||
模式 *o? | ||||
${var#pattern} | en source | |||
${var##pattern} | rce | |||
模式 ?e* | ||||
${var%pattern} | open sour | |||
${var%%pattern} | o |
使用的模式匹配与文件名通配符相同:*
匹配零个或多个任何字符,?
匹配恰好一个任何字符,[...]
括号引入针对单个字符的字符类匹配,支持否定(^
)以及 POSIX 字符类,例如 [[:alnum:]]
。 通过以此方式从我们的字符串中删除字符,我们可以在不知道我们需要的数据的偏移量的情况下获取子字符串
$ echo $PATH
/usr/local/bin:/usr/bin:/bin
$ echo "Lowest priority in PATH: ${PATH##*:}"
Lowest priority in PATH: /bin
$ echo "Everything except lowest priority: ${PATH%:*}"
Everything except lowest priority: /usr/local/bin:/usr/bin
$ echo "Highest priority in PATH: ${PATH%%:*}"
Highest priority in PATH: /usr/local/bin
参数扩展中的替换
相同的模式类型用于参数扩展中的替换。 替换通过 /
或 //
运算符引入,后跟两个参数,这些参数由另一个 /
分隔,表示模式和要替换的字符串。 模式匹配始终是贪婪的,因此运算符的加倍版本(在本例中)会导致变量扩展中所有匹配的模式都被替换,而单例版本仅替换最左侧的模式。
var="free and open" | ||||
---|---|---|---|---|
参数扩展 | 模式 [[:space:]] 字符串 _ |
|||
${var/pattern/string} | free_and open | |||
${var//pattern/string} | free_and_open |
大量的参数扩展修饰符将 Bash 变量和其他参数转换为强大的工具,而不仅仅是简单的值存储。 至少,在阅读 Bash 脚本时,了解参数扩展的工作原理非常重要,但我怀疑,像我自己一样,你们中的许多人也会喜欢这些扩展修饰符为您的脚本和交互式会话带来的简洁性和表现力。
4 条评论