What does `.[].foo[]` do in bash? Why does it match `..`?
The [
starts a set. A set is terminated by ]
. But there is a way to have ]
as part of the set, and that is to specify the ]
as the first character. As an empty set doesn't make any sense, this is not ambiguous.
So your examples are basically all a dot followed by a set that contains a dot, therefore it matches two dots.
The later examples don't find any files and are therefore returned verbatim.
Only quoted strings are not subject to globbing:
$ echo ".[].aliases[]"
.[].aliases[]
But un-quoted strings are subject to globbing. An unquoted string that contains an *
or a ?
or (valid) []
(bracket expression) will be modified by the list of files that match it. In the same way as a *
will transform into all the files in the matching directory and a ?
will match files of only one character, a (valid) []
will match files with the characters inside the brackets. A dot is a valid character:
$ echo a[.]b
a[.]b
$ touch "a.b"
$ echo a[.]b
a.b
To be able to match a ]
it should be the first character inside the brackets:
$ touch "a]b"
$ ls a[]]b
a]b
An empty bracket expression makes no sense (and is not expanded):
$ touch ab
$ ls a[]b
ls: cannot access 'a[]b': No such file or directory
That is why this works:
$ touch a]c abc afc azc a:c a?c aoc
$ ls a[]bfz:?]c
abc a:c a?c a]c afc azc
For [
the idea is similar:
$ touch a[c
$ ls a[[]c
a[c
but it could be at any position in a bracket expression:
$ ls a[]bf[z:?]c
abc a:c a?c a[c a]c afc azc
$ ls a[]bfz:?[]c
abc a:c a?c a[c a]c afc azc
The string you posted .[].foo[]
will match a dot followed by either a ]
, a .
, a f
, a o
or a [
. It is similar to:
$ echo a[].foo[]c
a[c a]c afc aoc
And it will match as follows:
$ touch .] .f .o .[ .a .b .z
$ echo .[].foo[]
.. .[ .] .f .o
Note that the directory entry ..
does not need to be created as it exists inside every directory by default. But a simple dot .
won’t be matched by a glob as it needs to be matched explicitly (by actually using a dot).
But that will not match ..aliases
as the bracket expression will only match one character. To match several characters you need to use a *
(anything):
$ touch ..a ..l ..i ..aliases ..alias ..ali
$ echo .[].aliases[]
.. .[ .] .a
$ echo .[].aliases[]*
.. .[ .] .a ..a ..ali ..alias ..aliases ..i ..l