How do I make bash's glob character classes case-sensitive?

4

I had created some files like knob_A.png and knob_a.png and my teammate on Windows said this caused problems with her app. I decided to call it knob_W.png instead of knob_a.png. Then I did an rsync up to our shared server. In order to clean things up I then did

rm knob_[a-d]*.png

and it removed knob_A.png too. This is wrong as a football bat.

Neither shopt -s nocaseglob nor shopt -u nocaseglob causes it to behave the way I want.

How do I tell bash to make its globs be case-sensitive like in the old days?

Mutant Bob

Posted 2017-06-26T15:08:40.983

Reputation: 342

Answers

7

Bash is being case sensitive here. The problem is with the sorting order of the characters in the range. From the Bash manual (info bash):

The sorting order of characters in range expressions is determined by the current locale and the value of the 'LC_COLLATE' shell variable, if set.

For example, in the default C locale, '[a-dx-z]' is equivalent to '[abcdxyz]'. Many locales sort characters in dictionary order, and in these locales '[a-dx-z]' is typically not equivalent to '[abcdxyz]'; it might be equivalent to '[aBbCcDdxXyYz]', for example. To obtain the traditional interpretation of ranges in bracket expressions, you can force the use of the C locale by setting the 'LC_COLLATE' or 'LC_ALL' environment variable to the value 'C'.

Try doing

export LC_COLLATE=C

xordspar0

Posted 2017-06-26T15:08:40.983

Reputation: 156

1you might want to expand the shell example to: LC_COLLATE=C; ls knob_[a-d]*.png . I'm used to doing things like LC_COLLATE=C ls knob_[a-d]*.png which does not work, probably because that only sets the environment of the subprocess and does not affect the bash that evaluated the glob. – Mutant Bob – 2017-06-27T17:30:55.580

1@MutantBob is correct: bash expands the glob before it sets LC_COLLATE but the main problem is that a simple X=Y sets a local shell variable X, which is not the same thing as an environment variable and LC_COLLATE is an environment variable. – Denis Howe – 2018-09-29T19:43:34.453

To set an environment variable temporarily for one command, say:

LC_COLLATE=C command

Note: no semicolon between the assignment and the command.

To set it for the rest of the session, say:

export LC_COLLATE=C

This will affect globs on subsequent lines. – Denis Howe – 2018-09-29T20:01:40.980

1

Bash has a shell option to preserve the pattern match so case isn't ignored during the expansion. You can add shopt -s globasciiranges to your script to enable case sensitive matches, and turn it off using shopt -u globasciiranges. See Bash Reference - Shopt Builtin

Alternatively you can use the LC_COLLATE=C as listed in the other answer, but make sure it has been exported to your environment with export LC_COLLATE

DonnieDarko

Posted 2017-06-26T15:08:40.983

Reputation: 11