What is the meaning of the ${0##...} syntax with variable, braces and hash character in bash?

BashVariablesSyntaxCurly Braces

Bash Problem Overview


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?

Bash Solutions


Solution 1 - Bash

See the section on Substring removal in the Advanced Bash-Scripting Guide‡:

> ${string#substring} > Deletes shortest match of substring from front of $string. > > ${string##substring} > > Deletes longest match of substring from front of $string.

The substring may include a wildcard *, matching everything. The expression ${0##/*} prints the value of $0 unless it starts with a forward slash, in which case it prints nothing.

‡ The guide, as of 3/7/2019, mistakenly claims that the match is of $substring, as if substring was the name of a variable. It's not: substring is just a pattern.

Solution 2 - Bash

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

Solution 3 - Bash

I can't believe I'm answering a teen-aged question, but 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.

Solution 4 - Bash

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.)

One might use it to 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)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser215997View Question on Stackoverflow
Solution 1 - BashMark ByersView Answer on Stackoverflow
Solution 2 - BashPaul CreaseyView Answer on Stackoverflow
Solution 3 - BashrasView Answer on Stackoverflow
Solution 4 - BashmwfearnleyView Answer on Stackoverflow