3

Given is a set of files in (sub)directories matching a string like in e.g.:

find -name 'string' | sort
./1/2/3/4/string
./1/2/3/string
./1/2/string
./1/string
./string

Why does using globstar to match the files like in

ls **string
string

fail to run through the directories, while

ls **/string
1/2/3/4/string  1/2/3/string  1/2/string  1/string  string

succeeds? From my understanding of the description in the man pages it should be matching (sub)directories, shuldn't it?

Question like this usually are marked as duplicate of "The result of ls * , ls ** and ls ***", yet the answer there only handles ls **, which indeed does recurse through directories. It seems as if a double asterisk followed by a string would break globstar.

Running GNU bash v.4.4.19(1) on mint 19


From man bash

globstar

If set, the pattern ** used in a pathname expansion context will match all files and zero or more directories and subdirectories. If the pattern is followed by a /, only directories and subdirectories match.

FelixJN
  • 13,566

2 Answers2

3

That's the way it works in Ksh and Zsh too.

$ mkdir -p a/b/cdir
$ touch    a/b/cfile
$ ksh -c 'set -o globstar; echo **c*'
**c*
$ zsh -c 'echo **c*'
zsh:1: no matches found: **c*

Under Pattern Matching, Bash's manual says:

When the globstar shell option is enabled, and * is used in a filename expansion context, two adjacent *s used as a single pattern will match all files and zero or more directories and subdirectories.

I suppose that could be read as meaning that "a single pattern" is a part separated by slashes, so foo/**/bar has it in a single pattern, but **bar doesn't.

It could be clearer, though.

A trailing slash after the whole pattern makes the pattern match just directories:

$ echo **/c*/
a/b/cdir/
ilkkachu
  • 138,973
  • Indeed in combination with @jeff-schaller 's comment on the source code, it seems like this is intended behaviour and that the documentation is a bit unclear in this point. – FelixJN Oct 10 '18 at 12:39
2

A double * decays to the effect of one * when it is not limited by (only) /:

$ ls *c
abc  ac  acc  bc  cc  dc

$ ls **c
abc  ac  acc  bc  cc  dc

Any character leading or trailing that is not a / makes the ** decay:

That is simply because a path name (as a file name) can not contain any /.
It is not possible for a * to expand to a name containing /. A ** is somewhat similar, it will expand to strings containing / only when alone or delimited by / (not any other character/string):

$ ls -d ./** | sort
./
./1
./1/2
./1/2/3
./1/2/3/4
./1/2/3/4/string
./1/2/3/string
./1/2/string
./1/string
./abc
./ac
./acc
./bc
./cc
./dc
./string

$ ls -d ./**c | sort 
./abc
./ac
./acc
./bc
./cc
./dc

$ ls -d ** | sort
1
1/2
1/2/3
1/2/3/4
1/2/3/4/string
1/2/3/string
1/2/string
1/string
abc
ac
acc
bc
cc
dc
string

$ ls -d **c | sort
abc
ac
acc
bc
cc
dc

$ ls -d **/ | sort
1/
1/2/
1/2/3/
1/2/3/4/

$ ls -d 1**/ | sort
1/