10

Consider this Bash script:

#!/bin/bash
echo Enter any character
read char
case $char in
    [a-z]) echo Lower case letter
            ;;
    [A-Z]) echo Upper case letter
            ;;
    [0-9]) echo Number
            ;;
    ?) echo Special char
            ;;
    *) echo You entered more than one character 
            ;;
esac

If I enter 'a', the output is Lower case letter, and it is the same for 'A'... How do I overcome this?

3
  • When you post a script make sure you use the code format, to keep the whitespace. Also, what's the actual question? I'm not sure what you mean...
    – AJefferiss
    Mar 13, 2015 at 12:30
  • 2
    @Arronical no need, echo can deal with reserved words echo if case then do .
    – terdon
    Mar 13, 2015 at 13:56
  • For a similar issue, but dealing with sort, see askubuntu.com/questions/597924/…
    – Joe
    Mar 22, 2015 at 20:39

2 Answers 2

21

The problem is that the character range [a-z] actually includes the upper case letters. This is explained in the bash manual:

Within a bracket expression, a range expression consists of two characters separated by a hyphen. It matches any single character that sorts between the two characters, inclusive. In the default C locale, the sorting sequence is the native character order; for example, ‘[a-d]’ is equivalent to ‘[abcd]’. In other locales, the sorting sequence is not specified, and ‘[a-d]’ might be equivalent to ‘[abcd]’ or to ‘[aBbCcDd]’, or it might fail to match any character, or the set of characters that it matches might even be erratic. To obtain the traditional interpretation of bracket expressions, you can use the ‘C’ locale by setting the LC_ALL environment variable to the value ‘C’.

To illustrate:

$ case B in [a-c]) echo YES;;  *) echo NO;; esac
YES
$ LC_ALL=C; case B in [a-c]) echo YES;; *) echo NO;; esac
NO

So, what happens is that in your locale (which is not C), [a-c] is actually [aAbBcC]. That's why you should use the POSIX character classes as suggested by @karel instead.

1
  • 4
    More precisely, you need to set LC_COLLATE to C, it's ok for other locale settings to be different. Setting LC_COLLATE to anything but C is rarely a good idea but sadly Ubuntu does it (it isn't the only culprit by far). Mar 13, 2015 at 20:48
20
#!/bin/bash
echo 'enter any character'
read char
case $char in
[[:lower:]]) echo 'lower case letter'
    ;;
[[:upper:]]) echo 'upper case letter'
    ;;
[0-9]) echo 'number'
    ;;
?) echo 'special char'
    ;;
*) echo 'u entered more than one char' 
    ;;
esac  

For more information about the lower case regular expression of [a-z] and the upper case regular expression of [A-Z] in bash see Why isn't the case statement case-sensitive when nocasematch is off?.

1
  • 6
    Following on from this, instead of [0-9] you can use [[:digit:]]. You can find more examples in man grep, or Google posix character classes. Mar 17, 2015 at 13:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .