Search code examples
bashfunctionargumentsglob

Matching brackets inside a regular expression does not work in bash


I defined a function that prints yes if its first argument contains a, b or c:

$ func() { [[ $1 =~ [abc] ]] && echo yes; }
$ func xyz
$ func xaz
yes
$ func xbz
yes
$ func xcz
yes

Now I would like to detect [, ] or * so I tried:

func() { [[ $1 =~ [\[\]*] ]] && echo yes; }
$ func x*z
$ func x[z
$ func x]z
yes

This is only seems to work for ]. I also tried set -f to disable glob expansion but got same results.


Solution

  • The behavior is very well explained in BRE/ERE Bracketed Expression section of POSIX regex specification:

    1. [...] The right-bracket ( ']' ) shall lose its special meaning and represent itself in a bracket expression if it occurs first in the list (after an initial circumflex ( '^' ), if any). Otherwise, it shall terminate the bracket expression, unless it appears in a collating symbol

    Which means, your ] needs to be present in your bracket expression at the beginning, may be after a ^, and not anywhere later in the expression. This is a special case that applies only for ] but not for [ which loses its special character inside a bracket expression.

    So defining

    [[ $1 =~ [][*] ]]
    

    should work as expected, because ] at the beginning is not terminating the bracket expression. Disabling glob expansion is of no consequence here, as * inside a bracket expression does not have a special meaning and only treated literally.