Search code examples
bashglob

bash file globbing anomaly


The bash manual (I'm using version 4.3.42 on OSX) states that the vertical bar '|' character is used as a separator for multiple file patterns in file globbing. Thus, the following should work on my system:

projectFiles=./config/**/*|./support/**/*

However, the second pattern gives a "Permission denied" on the last file that is in that directory structure so the pattern is never resolved into projectFiles. I've tried variations on this, including wrapping the patterns in parentheses,

projectFiles=(./config/**/*)|(./support/**/*)

which is laid out in the manual, but that doesn't work either.

Any suggestions on what I'm doing wrong?


Solution

  • You're probably referring to this part in man bash:

       If the extglob shell option is enabled using the shopt builtin, several
       extended pattern matching operators are recognized.  In  the  following
       description, a pattern-list is a list of one or more patterns separated
       by a |.  Composite patterns may be formed using one or more of the fol-
       lowing sub-patterns:
    
              ?(pattern-list)
                     Matches zero or one occurrence of the given patterns
              *(pattern-list)
                     Matches zero or more occurrences of the given patterns
              +(pattern-list)
                     Matches one or more occurrences of the given patterns
              @(pattern-list)
                     Matches one of the given patterns
              !(pattern-list)
                     Matches anything except one of the given patterns
    

    The | separator works in pattern-lists as explained, but only when extglob is enabled:

    shopt -s extglob
    

    Try this:

    projectFiles=*(./config/**/*|./support/**/*)
    

    As @BroSlow pointed out in a comment:

    Note that you can do this without extglob, ./{config,support}/**/*, which would just expand to the path with config and the path with support space delimited and then do pattern matching. Or ./@(config|support)/**/* with extglob. Either of which seems cleaner.

    @chepner's comment is also worth mentioning:

    Also, globbing isn't performed at all during a simple assignment; try foo=*, then compare echo "$foo" with echo $foo. Globbing does occur during array assignment; see foo=(*); echo "${foo[@]}"