Configuration Information [Automatically generated, do not change]:
Machine: aarch64
OS: darwin25.2.0
Compiler: gcc
Compilation CFLAGS: -g -O2
uname output: Darwin moxy.lan 25.3.0 Darwin Kernel Version 25.3.0: Wed Jan
28 20:54:55 PST 2026; root:xnu-12377.91.3~2/RELEASE_ARM64_T6031 arm64
Machine Type: aarch64-apple-darwin25.2.0

Bash Version: 5.3
Patch Level: 9
Release Status: release

Description:
  A negative extglob can match the empty string. Also, extglobs that appear
at the start of a pattern can match a dotfile, if they match a dot
explicitly.

  However, a negative extglob (or other extglob matching an empty string)
that comes ahead of a dot in a pattern will *not* allow that pattern to
match a dotfile. This seems inconsistent with the "Pathname expansion"
section, which states only that a '.' at the start (or immediatly following
a slash) must be "matched explicitly" unless dotglob is set. That is, it
seems that it *is* being "matched explicitly", but the path is not being
expanded as expected.

  It's of course possible that this is working as intended, in which case,
it could perhasp be "fixed" by an update to the manpage documentation to
clarify the cases where an extglob matching an empty string ahead of a '.'
character will prevent the filename from matching. (For context, I maintain
a glob implementation in JavaScript that aims to match Bash as a reference
implementation, so any guidance about intent would be extremely
appreciated!)

  Furthermore, if the extglob matches either the empty string *or* a dot
character, as in the case of `@(|.)`, then it *will* expand to include a
dotfile, even if it does not match the dot in that case. I'm struggling a
bit to figure out the intended logic here, if it's not just a bug.

  It seems like the extglob ahead of a matching '.' in the pattern will
prevent the match against a dotfile unless it *could* have matched a
leading '.', even if it actually *doesn't* match a leading '.' character in
the expansion, and it will also prevent a match unless it matches in the
normal way. Ie, an extglob like '@().foo' will not match '.foo', but
'@(|.).foo' will match '.foo', even though the '@(|.)' matches the empty
string in this case, not the '.' character, and so should be equivalent to
'@()'.

Repeat-By:

```
$ touch foo .foo
$ # expected: this does not match .foo, because !(a) cannot match .
$ echo !(a)foo
foo
$ # expected: @(|.) can match '.' or '', and appears at the start
$ echo @(|.)foo
.foo foo
$ # expected: @(|.) matches '', then .foo matches the rest
$ echo @(|.).foo
.foo
$ # wat? the '.' portion of the @() above should not have mattered!
$ echo @(|).foo
@(|).foo
$ # also, seems like this should match, if @(|.).foo matches
$ echo @(!(a)|.).foo
@(!(a)|.).foo
$ # matching an empty string ahead of '.' is very inconsistent:
$ echo @().foo
@().foo
$ echo !(a).foo
!(a).foo
$ echo !(a|.).foo
!(a|.).foo
$ # this is as expected, but weird that it works when others above don't?
$ echo @(!(a)|.)foo
.foo foo
$ # adding an empty string changes it too?
$ echo @(!(a)|.|).foo
.foo
```

Reply via email to