Search code examples
stringbashshellsubstringsh

How to search for a substring including wildcard passed through a variable in Bash?


Bash allows checking for a substring.

It is even possible to use the wildcard symbol * inside the search string:

# works
line="foo bar baz"
if [[ $line == *"foo "*" baz"* ]]; then
  echo "found"
fi

1.) But how do I pass the search string including the wildcard through a variable?

# fails
line="foo bar baz"
search='foo "*" baz'
echo "$search"
if [[ $line == *"$search"* ]]; then
  echo "found"
fi

2.) And if it is possible through escaping: How do I manipulate $search, so the user of my script can simply pass foo * baz?

The only solution I found is this, but it is limited to a specific maximum amount of wildcards and it does not feel like the best way to solve this:

line="foo bar baz"
search="foo * baz"
IFS=\* read -r search1 search2 search3 search4 <<< "$search"
if [[ $line == *"$search1"*"$search2"*"$search3"*"$search4"* ]]; then
  echo "found"
fi

Solution

  • This is actually simple: don't quote the variable containing the search pattern, or include quotes in the search variable (unless you want to search for literal quotes).

    line="foo bar baz"
    search="foo * baz"
    if [[ $line == *$search* ]]; then
      echo "found"
    fi
    

    Explanation: if parts of the pattern (i.e. the thing on the right side of the = or == operator in [[ ]]) are quoted, they're treated as literal rather than as glob patterns. Thus, putting double-quotes around $search makes bash treat the * in its value as just a literal character rather than a wildcard. Removing the double-quotes makes bash treat it as a wildcard.

    Note that this doesn't apply to quotes in the variable's value; those will be treated as literal whether or not the variable is quoted. So if you use search='foo "*" baz', it's going to be looking for double-quotes in the line whether or not you double-quote $search.