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

   local x='test'
   if [ $x = '*test*' ]; then
      echo 'hello'

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 
  • 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#Filename-Generation – Tyler Kelly Nov 22 '19 at 14:23
  • zsh.sourceforge.net/Doc/Release/Conditional-Expressions.html see edit above as well. – Tyler Kelly Nov 22 '19 at 14:25
  • 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 – Tyler Kelly Nov 22 '19 at 14:26

[ $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.

| improve this answer | |
  • Or use the standard case $x in (*test*) ... ;esac syntax. – Stéphane Chazelas Nov 22 '19 at 17:57

*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'
| improve this answer | |
  • 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 [. – Gilles 'SO- stop being evil' Nov 22 '19 at 17:43
  • 1
    @Gilles'SO-stopbeingevil': I'm no zsh expert and OP said they wanted regex. – jesse_b Nov 22 '19 at 17:45
  • 1
    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 Nov 22 '19 at 17:54

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.