Pattern matching in a zsh conditional expression

12,522

Solution 1

[ $x = '*test*' ] tests whether the string resulting from expanding $x, which is text, is equal to the string resulting from expanding '*test*', which is *text*.

To test whether the value of the variable x matches the pattern *test*, you need to use the = or == operator of zsh conditional expressions, which are written within double brackets [[ … ]]. Furthermore special characters in the pattern must be unquoted, otherwise they stand for themselves. Thus:

if [[ $x == *test* ]]; then …

The syntax of conditional expressions is similar to the syntax of expressions that you can use within single brackets [ … ], but not identical. [ is parsed like an ordinary command; in fact, it's a built-in command with a one-character name, which is identical to the test builtin except that [ requires an additional argument at the end which must be ]. [[ … ]] is a distinct grammatical construct, which allows it to have shell special characters inside. [ $x = *test* ] would expand *test* to the list of matching file names (globbing) and the test builtin would end up parsing the result of that. [[ $x = *test* ]] parses *test* as part of conditional expression parsing which does not invoke globbing.

Solution 2

*test* is not a valid regex pattern. The * is a repetition operator and needs something to repeat. It's very likely you want .*test.*, although that isn't necessary with regex since it is not anchored by default. You could just look for test

However you cannot match regex patterns with the = operator, you need =~.

precmd () {
   local x=test
   if [[ $x =~ test ]]; then
      echo 'hello'
   fi
}
Share:
12,522
Tyler Kelly
Author by

Tyler Kelly

Updated on September 18, 2022

Comments

  • Tyler Kelly
    Tyler Kelly over 1 year

    I am having trouble with pattern matching in zsh's hook function precmd. I have the following:

    precmd(){
       local x='test'
       if [ $x = '*test*' ]; then
          echo 'hello'
       fi
    }
    

    which does not print hello ever. I've tested this code with a normal zsh test.zsh that works fine, and I've tried w/o the regex in precmd and got things to print out fine as well. Any clue as to why this isn't working as expected?

    $ zsh --version
    zsh 4.3.11 RHEL 
    
    • Tyler Kelly
      Tyler Kelly over 4 years
      hook functions: http://zsh.sourceforge.net/Doc/Release/Functions.html and regex is a bit of a misnomer, actually a globbing pattern: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Filena‌​me-Generation
    • Tyler Kelly
      Tyler Kelly over 4 years
    • Tyler Kelly
      Tyler Kelly over 4 years
      the content in the function works outside of that function as stated in the Q, so it seems like I am using their pattern matching correctly. this just doesn't work properly when placed in the precmd function
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 4 years
    Why switch to regexp matching with =~? You can keep the wildcard syntax and the = operator. The only thing that needs to change is to use [[ instead of [.
  • jesse_b
    jesse_b over 4 years
    @Gilles'SO-stopbeingevil': I'm no zsh expert and OP said they wanted regex.
  • Stéphane Chazelas
    Stéphane Chazelas over 4 years
    That's more a bash (3.2+) answer. In zsh, you can quote the regexps, and generally want to if they contain special characters. Use [[ $x =~ '.*test.*' ]] or [ "$x" '=~' '.*test.*' ] or [[ $x = *test* ]] (no quote there), or [[ $x =~ test ]]
  • Stéphane Chazelas
    Stéphane Chazelas over 4 years
    Or use the standard case $x in (*test*) ... ;esac syntax.