78

I just saw some code in bash that I didn't quite understand. Being the newbie bash scripter, I'm not sure what's going on.

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

I don't really see a difference in output in these two commands (prints the script name). Is that # just a comment? And what's with the /*. If it is a comment, how come it doesn't interfere with the closing } brace?

Can anyone give me some insight into this syntax?

Paŭlo Ebermann
  • 73,284
  • 20
  • 146
  • 210
user215997
  • 1,267
  • 2
  • 12
  • 12

4 Answers4

101

See the section on Substring removal on the parameter expansion page of the bash-hackers' wiki:

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

This form is to remove the described pattern trying to match it from the beginning of the string. The operator # will try to remove the shortest text matching the pattern, while ## tries to do it with the longest text matching.

Example string (just a quote from a big man):

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

Syntax Result
${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.
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • Would you mind adding a link to the relevant section in the Bash reference manual to this? For example https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion – Benjamin W. Jan 31 '21 at 00:24
  • 1
    As also suggested by the footnote and the previous comment, the ABS is not a good canonical reference; this answer could be improved by citing authoritative or at least respected sources instead. – tripleee Mar 17 '22 at 06:53
  • Consider the objections to linking the ABS reiterated; it teaches a lot of antipatterns and bad practices. Another _good_ reference is https://wiki.bash-hackers.org/syntax/pe. – Charles Duffy Jun 11 '22 at 14:57
37

Linux tip: Bash parameters and parameter expansions

${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
Paul Creasey
  • 28,321
  • 10
  • 54
  • 90
5

I think the existing answers (while certainly accurate) miss the practical thrust of the OP's question.

The OP asked about:

echo ${0##/*}

My guess is that what they really saw in the code was:

echo ${0##*/}

The latter essentially means "delete everything up to, and including, the last slash (if any)". So it's a concise way of getting the name of the script without the path, regardless of how the script was called. It's equivalent* to

basename "$0"

but is arguably handier (and more efficient) if you're using it as a variable rather than just printing it to the console. (OTOH basename is more portable, whereas the parameter expansion is a bashism.)

* More or less. There are edge cases (such as file names that start with a space) where they don't output exactly the same thing.

Matt Seymour
  • 8,880
  • 7
  • 60
  • 101
ras
  • 51
  • 1
  • 3
2

Did you mean ##/*, or ##*/?

##/*

${0##/*} is a bit unusual - it will strip off the prefix /... from the start of $0.

It's an all-or-nothing operation: If $0 starts with a slash (e.g. /home/bob/myscript.sh), then it will strip everything and return an empty string. Otherwise (e.g. ./myscript.sh) it will strip nothing and return the the whole of $0.

(The double ## indicates that it should strip the longest match; a single # would only strip the first character, if it's a slash.)

I'm not sure how useful it is. Perhaps it could be used to help detect if a script is called from an absolute path or not.

##*/

${0##*/} is more common - it will will strip off the prefix .../ from the start of $0.

e.g. if $0 is /home/bob/myscript.sh, it will return myscript.sh.

The ## again indicates that it should strip the longest match, so it will strip all slashes (.../.../).
(As opposed to a #, which will strip the first slash only, e.g. /home/bob/myscript.sh -> home/bob/myscript.sh, a/b/myscript.sh -> b/myscript.sh)

mwfearnley
  • 3,303
  • 2
  • 34
  • 35