Inconsistent handling of patterns with backslah (`\`) escaping

Andrei Borzenkov <[email protected]> Sun, 31 May 2026 08:53:22 +0300
Newsgroups gmane.comp.shells.bash.bugs
Message-ID <[email protected]>
Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -fcf-protection -O2 -Wall -U_FORTIFY_SOURCE 
-D_FORTIFY_SOURCE=3 -fstack-protector-strong -funwind-tables 
-fasynchronous-unwind-tables -fstack-clash-protection 
-Werror=return-type -flto=auto -g  -D_GNU_SOURCE -DRECYCLES_PIDS -Wall 
-g -Wuninitialized -Wextra -Wno-switch-enum -Wno-unused-variable 
-Wno-unused-parameter -Wno-parentheses -ftree-loop-linear -pipe 
-DBNC382214=0 -DIMPORT_FUNCTIONS_DEF=0 
-DDEFAULT_LOADABLE_BUILTINS_PATH='/usr/lib64/bash'
uname output: Linux uefi 7.0.10-2-default #1 SMP PREEMPT_DYNAMIC Sat May 
23 12:09:09 UTC 2026 (bb95589) x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-suse-linux-gnu

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

Description:

Consider:

$ ls *
*  x
$

So, two files - the first with the name '*', the second irrelevant.

$ bar='\**'
$ ls $bar
*
$

So far so good - pathname expansion produces expected result. In the 
pattern "\*" matches the literal "*".

$ bar='\*'
$ ls $bar
ls: cannot access '\*': No such file or directory
$

Oops. Why it suddenly attempts to match *two* characters "\*" instead of 
just one literal "*"?

It is even more confusing as in other places where patterns are used the 
"\*" pattern apparently works correctly:

$ foo='abc:*'
$ echo ${foo%:$bar}
abc
$ [[ $foo == abc:$bar ]] && echo yes || echo no
yes
$


Repeat-By:

Assign a glob pattern with all special characters escaped by the 
backslash ('\') and use this variable on the command line. Observe that 
shell does not remove backslashes from the pattern so the result does 
not match the existing files.

$ touch '***'
$ foo='\*\*\*'
$ ls $foo
ls: cannot access '\*\*\*': No such file or directory
$ ls '***'
***
$