参数扩展 `${parameter#word}` 未按预期工作

参数扩展 `${parameter#word}` 未按预期工作

我目前正在尝试理解参数扩展,特别是可以通过模式匹配删除部分参数值的不同形式。针对这个问题,我们先来关注一下扩展${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}模式,找到最短的匹配(匹配任何数量的任何字符),删除它并扩展到。类似地,会找到最长的匹配,,在扩展处将其删除。*ofoobarfo*obar${str##*o}foobar

$ 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}"等等。

相关内容