2

When dealing with with nested parameter expansions, which of these is the correct way to quote things?

This one:

var="${var#"${var%%[![:space:]]*}"}"

Or this one:

var="${var#${var%%[![:space:]]*}}"

I have seen it done both ways. The first variation is a snippet from a Stack Overflow answer. It is one of the most highly upvoted answers to the question (the question being about how to trim whitespace from a variable).


I'm also having trouble finding a reputable source that addresses how to quote things when working with a "word" or "pattern" that is a variable instead of literal text. Of the two below, which is correct?

This one:

strip_ext="${filename%."$filename_ext"}"

Or this one:

strip_ext="${filename%.$filename_ext}"
1

In both cases, the first way is usually what you want. When the string on the right side of ${var#*a} is not quoted it is interpreted/expanded as a pattern. In this example removing the leading text up to the first letter a. If quoted: ${var#"*a"} the expansion would remove a literal *a. That may be a problem if in:

"${filename%."$filename_ext"}"

the "$filename_ext" may contain pattern characters. Similarly, if $var in "${var#"${var%%[![:space:]]*}"}" could contain pattern characters (*?[) and you do not want them interpreted as a pattern, then quote the expansion as:

"${var#"${var%%[![:space:]]*}"}"

Note that the right side of a variable assignement is considered quoted and doesn't need (but cause no harm) quotes, this two are also correct:

      var=${var#"${var%%[![:space:]]*}"}
strip_ext=${filename%."$filename_ext"}
3

It seems to depend on how you want the right-hand side of the expansion interpreted. As a pattern, or as a literal string.

Here, the unquoted * is a pattern, and eats the whole string. The quoted one just removes a literal asterisk:

var='foo*'
echo ":${var%%*}:"                 # this prints ::
echo ":${var%%"*"}:"               # this prints :foo:

It's the same if the put the pattern through a variable:

var='foo*'
pat='*'
echo ":${var%%$pat}:"              # this prints ::
echo ":${var%%"$pat"}:"            # this prints :foo:

Or with another parameter expansion on the right side:

var='foo*'
pat='x*x'
echo ":${var%%${pat//x}}:"         # this prints ::
echo ":${var%%"${pat//x}"}:"       # this prints :foo:

(${pat//x} results in just * again.)

I get the same behaviour with all POSIX-like shells I tried (Bash, ksh, yash). Zsh is different of course, all parameter expansions on the right side act like they're quoted there, like they do in relation to word-splitting issues too. I suppose there's some operator that can be used in an expansion to change that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.