Skip to content
Joachim Ansorg edited this page Nov 15, 2021 · 2 revisions

Expansions inside ${..} need to be quoted separately, otherwise they will match as a pattern.

Problematic code:

relative_path() {
  printf '%s\n' "${2#$1}"
}

# Results in "/tmp/King_Kong_[1933]/extras/trailer.mkv" because the prefix fails to match
relative_path "/tmp/King_Kong_[1933]/" "/tmp/King_Kong_[1933]/extras/trailer.mkv"

# Results in "cover.jpg" even though the prefix is different
relative_path "/tmp/King_Kong_[1933]/" "/tmp/King_Kong_3/cover.jpg"

Correct code:

relative_path() {
  printf '%s\n' "${2#"$1"}"
}
# Results in "extras/trailer.mkv" as expected
relative_path "/tmp/King_Kong_[1933]/" "/tmp/King_Kong_[1933]/extras/trailer.mkv"

# Results in "/tmp/King_Kong_3/cover.jpg" as expected
relative_path "/tmp/King_Kong_[1933]/" "/tmp/King_Kong_3/cover.jpg"

Rationale:

When using expansions in a parameter expansion prefix/suffix expression, the expansion needs to be quoted separately or it will match as a pattern. The quotes around the outer parameter expansion does not protect against this.

This means that any variable that contains e.g. brackets, asterisks or question marks may not match as expected. In the example, [1933] was interpreted as a pattern character range and would therefore match /tmp/King_Kong_3/ but not /tmp/King_Kong_[1933]/ as was the intention.

Exceptions:

If you wanted to treat the string as a pattern, such as suffix=".*"; file="${var%$suffix}"; then you can ignore this suggestion.

Related resources:

  • Help by adding links to BashFAQ, StackOverflow, man pages, POSIX, etc!

ShellCheck

Each individual ShellCheck warning has its own wiki page like SC1000. Use GitHub Wiki's "Pages" feature above to find a specific one, or see Checks.

Clone this wiki locally