Bash – Case sensitivity in square-bracket globbing

bashlocaleshellwildcards

Normally, bash globbing is case sensitive:

$ echo c*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo C*
CarePackage.md ChocRippleCake.md Clips

Using square brackets doesn't seem to change this:

$ echo [c]*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo [C]*
CarePackage.md ChocRippleCake.md Clips

It still doesn't change it if a hyphen is used:

$ echo [c-c]*
casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py
$ echo [C-C]*
CarePackage.md ChocRippleCake.md Clips

But the letters are interspersed:

$ echo [B-C]*
CarePackage.md casefix.pike cdless chalices.py charconv.py chocolate.pike ChocRippleCake.md circum.py clip.pike Clips cpustats.pike crop.pike cwk2txt.py
$ echo [b-c]*
beehive-anthem.txt bluray2mkv.pike branch branchcleanup.pike burdayim.pike casefix.pike cdless chalices.py charconv.py chocolate.pike circum.py clip.pike cpustats.pike crop.pike cwk2txt.py

This suggests that the hyphen is using a locale order, "AaBbCcDd". So: is there any way to glob for all files that begin with an uppercase letter?

Best Answer

In bash version 4.3 and later, there is a shopt option called globasciiranges :

According to shopt builtin gnu man pages:

globasciiranges
If set, range expressions used in pattern matching bracket expressions (see Pattern Matching) behave as if in the traditional C locale when performing comparisons. That is, the current locale’s collating sequence is not taken into account, so ‘b’ will not collate between ‘A’ and ‘B’, and upper-case and lower-case ASCII characters will collate together.

As a result you can

$ shopt -s globasciiranges 
$ echo [A-Z]*

Use shopt -u for disabling.

Another way is to change locale to C. You can do this temporarily using a subshell:

$ ( LC_ALL=C ; printf '%s\n' [A-Z]*; )

You will get the results you need, and when the sub shell is finished, the locale of your main shell remains unchanged to whatever was before.

Another alternative is instead of [A-Z] to use brace expansion {A..Z} together with nullglob bash shopt option.

By enabling the nullglob option, if a pattern is not matched during pathname expansion, a null string is returned instead of the pattern itself.
As a result this one will work as expected:

$ shopt -s nullglob;printf '%s\n' {A..Z}*
Related Question