tl;dr - Bash expansion is complicated to prevent infinite symlink loops (in
bash >= 4.3), and you and I both misinterpreted what it was doing in the commands you posted
I assume you have
bash >= 4.3 as I cannot reproduce what you describe in
bash 4.2.46, it loops until it hits a recursion limit (as expected).
Stared at this for a while and set up a test directory to play in that imitaed your situation. The crux of this is how the bash expansion happens in each of your examples. The expansion behaves differently based on whether or not it is followed by a
/, and there's just some cognitive dissonance on that point for us primates when looking at examples like this.
From the documentation for bash shopt:
If set, the pattern ‘**’ used in a filename 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.
To illustrate here's my test setup:
$ mkdir -p test/nested
$ cd test
$ touch sneaky
$ touch nested/sneaky
$ cd nested
$ ln -s . looper
$ cd ..
resulting in this directory structure:
- looper -> ./
This duplicates your findings in my test directory:
$ for apath in ../**/sneaky; do echo "$apath"; done
$ printf "%s\n" ../** | grep sneaky
In the first example, the glob expands to
(test/nested/looper, test/nested, test), stopping at
looper without following the link because the glob was followed by a
We then append
/sneaky to that, resulting in the set
(test/nested/looper/sneaky, test/nested/sneaky, test/sneaky).
In the second example, the glob expands to
(test/nested/looper, test/nested/sneaky, test/nested, test/sneaky, test) (which you can verify by removing the
| grep sneaky)
Again, this expansion does not follow the
looper link, but in this case we do not append
/sneaky to it, thus dropping
../test/nested/looper/sneaky from our results.
On the other hand, we continue to get
../test/sneaky because the glob grabs files as well when it is not followed by a