3

I want to check if a string's first char is uppercase, lowercase or anything else. I tried this code but I can't get to the last else although the first two conditions are false.

#!/bin/bash

echo "enter var: "
read var

if [[ {$var::1 =~ [A-Z] ]]
then 
echo "UpperCase"

elif [[ {$var::1} =~ [a-z] ]]
then 
echo "LowerCase"

else 
echo "Digit or a symbol"

fi

exit

When I enter 1hello I get: "LowerCase"

What am I missing here?!

2 Answers 2

6

You don't necessarily need to extract the first character, you can compare the whole string to a pattern.

Here, I'm using the POSIX character classes [:upper:] and [:lower:] which I find more descriptive. They also handle non-ASCII letters.

Regex matching:

if   [[ $var =~ ^[[:upper:]] ]]; then echo starts with an upper
elif [[ $var =~ ^[[:lower:]] ]]; then echo starts with a lower
else                                  echo does not start with a letter
fi

With shell glob patterns -- within [[...]] the == operator does pattern matching not just string equality

if   [[ $var == [[:upper:]]* ]]; then echo starts with an upper
elif [[ $var == [[:lower:]]* ]]; then echo starts with a lower
else                                  echo does not start with a letter
fi

A case statement would work here as well

case "$var" in
    [[:upper:]]*) echo starts with an upper ;;
    [[:lower:]]*) echo starts with a lower ;;
               *) echo does not start with a letter ;;
esac

Sign up to request clarification or add additional context in comments.

Comments

1

Neither of your parameter expansions are correct. {$var::1 evaluates to {1hello::1, not 1, and {$var::1} likewise evaluates to {1hello::1}.

The expansion you want is ${var::1}, which does expand to 1 as intended.


You don't need a fancy parameter expansion anyway; you can match against the first character using regular expressions alone

 [[ $var =~ ^[a-z] ]]

or pattern-matching

[[ $var = [a-z]* ]]

Regular expressions are not implicitly anchored, so you can use ^ to explicitly match the beginning of the string; the remainder of the string can be ignored.

Pattern matches are implicitly anchored to the start and end of the string, so you need * to match everything (if anything) that follows the initial character of the string.

1 Comment

Better to use [[:lower:]] rather than [a-z] for better behavior across locales; for the ones where collation order is AaBbCcDd...Zz, a-z isn't going to behave as-desired.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.