Bash 参数展开简介

通过这份关于展开修饰符的快速入门指南开始学习,这些修饰符可以将 Bash 变量和其他参数转换为超越简单值存储的强大工具。
633 位读者喜欢这篇文章。
open source button on keyboard

Opensource.com

在 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

使用的模式匹配与文件名 globbing 相同:* 匹配零个或多个任意字符,? 匹配一个任意字符,[...] 方括号引入针对单个字符的字符类匹配,支持否定 (^) 以及 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 脚本时,理解参数展开的工作原理非常重要,但我怀疑与我一样,你们中的许多人会喜欢这些展开修饰符为您的脚本以及您的交互式会话带来的简洁性和表达性。

标签
james pannacciulli
James Pannacciulli 是软件自由和用户自主权的倡导者,拥有语言学硕士学位。他受聘为系统工程师,在业余时间偶尔在各种会议上就 bash 用法发表演讲。 James 喜欢酸啤酒和刺痛的荨麻。

4 条评论

只是想对这篇文章表示感谢。这是我第一次看到关于参数展开的实用指南。

多么精彩的文章。很高兴在一个易于参考的页面上拥有所有这些内容。谢谢!

在 "var=opensource" 表格中...
模式为 *o?
"${var#pattern} en source" - 结果应该是 "pen source"
"${var##pattern} rce" - 结果应该是 "urce"

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.