Search code examples
bashglob

Bash negative wildcard using sub shell with `bash -c`


In bash, I can use a negative wildcard to glob all files in a directory that don't match some pattern, for example:

echo src/main/webapp/!(WEB-INF)

This works fine.

However, if I try to use exactly the same wildcard with bash -c to pass the command as an argument to a new bash shell, I get a syntax error:

$ bash -c 'echo src/main/webapp/!(WEB-INF)'
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `echo src/main/webapp/!(WEB-INF)'

Note that if I use a different glob, like bash -c 'echo src/main/webapp/*' it works as expected.

Why doesn't bash accept the same negative glob with -c as it does when run normally, and how can I get it to accept this negative glob?


Solution

  • That's because !(..) is a extended glob pattern that is turned on by default in your interactive bash shell, but in an explicit sub-shell launched with -c, the option is turned off. You can see that

    $ shopt | grep extglob
    extglob         on
    $ bash -c 'shopt | grep extglob'
    extglob         off
    

    One way to turn on the option explicitly in command line would be to use the -O flag followed by the option to be turned on

    $ bash -O extglob -c 'shopt | grep extglob'
    extglob         on  
    

    See extglob on Greg's Wiki for the list of extended glob patterns supported and The Shopt Builtin for a list of the extended shell options and which ones are enabled by default.