在bash中,带有变量、大括号和井号字符的${0##...}语法的含义是什么?

80

我刚刚看到一些bash代码,但并不太理解。作为一个新手bash脚本编写者,我不确定正在发生什么。

echo ${0##/*}
echo ${0}

我其实看不出这两个命令的输出有什么不同(都输出脚本名称)。那个#只是注释吗?还有那个/*是干嘛用的。如果它是注释,为什么它不会影响到结束的}大括号呢?

有人能给我解释一下这种语法吗?

4个回答

102
请参阅bash-hackers' wiki的参数扩展页面上的子字符串删除部分:

${PARAMETER#PATTERN}${PARAMETER##PATTERN}

此形式是从字符串开头尝试匹配并删除所描述的模式。运算符#将尝试删除最短的匹配模式,而##则尝试使用最长的匹配模式。

示例字符串(来自一位大人物的引用):

MYSTRING="Be liberal in what you accept, and conservative in what you send"

语法 结果
${MYSTRING#*in} Be liberal in what you accept, and conservative in what you send.
${MYSTRING##*in} Be liberal in what you accept, and conservative in what you send.

您介意在此添加到Bash参考手册相关章节的链接吗?例如https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion - Benjamin W.
1
正如脚注和前面的评论所建议的那样,ABS不是一个好的权威参考;相反,引用权威或至少受人尊敬的来源可以改善这个答案。 - tripleee
考虑到反对链接ABS的观点,它教授了很多反模式和不良实践。另一个好的参考资料是https://wiki.bash-hackers.org/syntax/pe。 - Charles Duffy

38

Linux 小技巧:Bash 参数和参数展开

${PARAMETER##WORD}  Results in removal of the longest matching pattern from the beginning rather than the shortest.
for example
[ian@pinguino ~]$ x="a1 b1 c2 d2"
[ian@pinguino ~]$ echo ${x#*1}
b1 c2 d2
[ian@pinguino ~]$ echo ${x##*1}
c2 d2
[ian@pinguino ~]$ echo ${x%1*}
a1 b
[ian@pinguino ~]$ echo ${x%%1*}
a
[ian@pinguino ~]$ echo ${x/1/3}
a3 b1 c2 d2
[ian@pinguino ~]$ echo ${x//1/3}
a3 b3 c2 d2
[ian@pinguino ~]$ echo ${x//?1/z3}
z3 z3 c2 d2

一些针对像我这样的初学者的解释可能更好,例如这些模式是如何工作的。 - Muhammad Awais

5

我认为现有的答案(虽然肯定是准确的)忽略了OP问题的实际重点。

OP询问的内容:

echo ${0##/*}

我猜他们在代码中真正看到的是:

echo ${0##*/}

后半句话的意思是“删除到最后一个斜杠(如果有的话)以及包括它在内的所有内容”。因此,这是一种简洁的方法,可以获取脚本名称而不考虑脚本的调用方式。它等价于:
basename "$0"

但是如果你将它用作变量而不仅仅是将其打印到控制台,那么它可能更加方便(和高效)。(另一方面,基本名称更具可移植性,而参数扩展则是Bashism。)

* 或多或少。 在一些边缘案例(例如以空格开头的文件名),它们输出的内容可能不完全相同。


2

您是想说 ##/* 还是 ##*/

##/*

${0##/*} 是一种有点不同寻常的方法 - 它将从 $0 的开头删除前缀 /...

这是一种全盘操作:如果 $0 以斜杠开头(例如:/home/bob/myscript.sh),那么它将删除所有内容并返回一个空字符串,否则(例如:./myscript.sh)就不会删除任何内容并将返回 $0 的全部内容。

(双重##表示它应该删除最长匹配;单个#只会删除首字符,如果它是斜杠。)

我不确定它有多有用。也许它可以用于帮助检测脚本是否从绝对路径调用。

##*/

${0##*/} 更为常见 - 它将从 $0 的开头删除前缀 .../

例如,如果 $0/home/bob/myscript.sh,它将返回 myscript.sh

再次使用##表示它应该删除最长的匹配项,因此它将删除所有斜杠(.../.../)。
(相对于#,它只会删除第一个斜线,例如:/home/bob/myscript.sh -> home/bob/myscript.sha/b/myscript.sh -> b/myscript.sh。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接