我目前正在尝试理解参数扩展,特别是可以通过模式匹配删除部分参数值的不同形式。针对这个问题,我们先来关注一下扩展${parameter#word}
。
手册中的两个相关部分(man bash
)(重点是我的):
${parameter#word}
${parameter##word}
删除匹配的前缀模式。这单词被扩展以产生一个模式,就像路径名扩展,并使用下面的模式匹配中描述的规则与参数的扩展值进行匹配。如果模式与parameter值的开头匹配,则扩展的结果是parameter的扩展值,并删除最短匹配模式(case'#'
)或最长匹配模式('##'
case)。 [...]
路径名扩展
分词后,除非设置了 -f 选项,否则 bash 会扫描每个单词中的字符 *、? 和 [。如果出现这些字符之一,则该单词被视为一种模式,并替换为按字母顺序排序的与该模式匹配的文件名列表(请参阅下面的模式匹配)。 [...]
但是,请考虑终端会话的以下部分:
root@cerberus ~/scripts # mkdir test
root@cerberus ~/scripts # cd test
root@cerberus ~/scripts/test # touch foo
root@cerberus ~/scripts/test # String=foobar
root@cerberus ~/scripts/test # printf '%s\n' ${String#foo}
bar
root@cerberus ~/scripts/test # printf '%s\n' ${String#*}
foobar
root@cerberus ~/scripts/test #
我无法理解最后一个命令后的输出。根据手册,*
应该扩展为foo
,因为foo
它是当前目录中唯一的文件;至少,这是我的概念“就像路径名扩展一样产生模式”。
因此,在这种情况下,printf '%s\n' ${String#*}
应该给出与 相同的输出printf '%s\n' ${String#foo}
。
显然,事实并非如此。我的理解错在哪里?
答案1
关于模式匹配:
*
匹配任何字符串,包括空字符串。
并且,在用于子字符串删除的参数扩展中,#
执行最短匹配,而##
最长匹配。
这是从开头删除空字符串(最短匹配):
$ printf '%s\n' "${String#*}"
foobar
这将foobar
从头开始删除(最长的匹配):
$ printf '%s\n' "${String##*}"
$
关于描述的最后一段:使用参数扩展删除子字符串与当前目录中的文件无关。该模式匹配参数值,而不是任何文件,将其视为对参数值的文本处理操作。
答案2
它采取“单词“ 部分作为模式喜欢用于文件名生成的那些,但它不使用它用于文件名生成。相反,那里的模式用于匹配给定参数的值。
例如,如果你有str=foobar
,那么会尝试开头的${str#*o}
模式,找到最短的匹配(匹配任何数量的任何字符),删除它并扩展到。类似地,会找到最长的匹配,,在扩展处将其删除。*o
foobar
fo
*
obar
${str##*o}
foo
bar
$ str=foobar
$ echo ${str#*o}
obar
$ echo ${str##*o}
bar
原因是“单词已扩展”,您可以在“中使用扩展单词”部分,像这样:
$ str=foobar
$ char=o
$ echo ${str#*$char}
obar
或者
$ pat="*o"
$ echo ${str#$pat}
obar
话又说回来,如果像上面那样不加引号的扩展,删除前缀后的结果将经历分词和通配符。你可能应该引用整个扩展来防止这种情况,即"${str#*o}"
等等。