bashcase-conversion

Bash ,, and ^^ case conversion are not matching patterns


I have been experimenting with the bash internal case conversion and found what I believe are inconsistencies.

I define a variable with the value "aBcDeF" and try converting the first upper case character in the range [A-D] to lower case. I would expect a result of "abcDeF" but instead, I get:

$ lowerFirst=aBcDeF
$ echo ${lowerFirst,[A-D]}
aBcDeF

It appears to be matching the lower case "a", seeing that it is lower case already and concluding there is nothing to do.

The bash man page says

${parameter,pattern}  

The pattern is expanded to produce a pattern just as in filename expansion. Each character in the expanded value of parameter is tested against pattern, and, if it matches the pattern, its case is converted. The pattern should not attempt to match more than one character.

This lead me to study the section on filename expansion which seems pretty clear that there should be a match on the first uppercase character.

This got me wondering about the case conversion, generally and things don't work the way the man page seems to indicate.

My script:

lowerFirst=aBcDeF
upperFirst=AbCdEf

echo 'lowerFirst=aBcDeF'
echo 'upperFirst=AbCdEf'
echo
echo 'Original Converted    Expected    Conversion'
echo "${lowerFirst}   ${lowerFirst,[A-D]}       abcDeF      "'${lowerFirst,[A-D]}'
echo "${upperFirst}   ${upperFirst,[A-D]}       abCdEf      "'${upperFirst,[A-D]}'

echo "${lowerFirst}   ${lowerFirst,[a-d]}       aBcDeF      "'${lowerFirst,[a-d]}'
echo "${upperFirst}   ${upperFirst,[a-d]}       AbCdEf      "'${upperFirst,[a-d]}'

echo "${lowerFirst}   ${lowerFirst^[A-D]}       aBcDeF      "'${lowerFirst^[A-D]}'
echo "${upperFirst}   ${upperFirst^[A-D]}       AbCdEf      "'${upperFirst^[A-D]}'

echo "${lowerFirst}   ${lowerFirst^[a-d]}       abcDeF      "'${lowerFirst^[a-d]}'
echo "${upperFirst}   ${upperFirst^[a-d]}       abCdEf      "'${upperFirst^[a-d]}'

echo
echo "${lowerFirst}   ${lowerFirst,,[A-D]}       abcdeF      "'${lowerFirst,,[A-D]}'
echo "${upperFirst}   ${upperFirst,,[A-D]}       abcdEf      "'${upperFirst,,[A-D]}'

echo "${lowerFirst}   ${lowerFirst,,[a-d]}       aBcDeF      "'${lowerFirst,,[a-d]}'
echo "${upperFirst}   ${upperFirst,,[a-d]}       AbCdEf      "'${upperFirst,,[a-d]}'

echo "${lowerFirst}   ${lowerFirst^^[A-D]}       aBcDeF      "'${lowerFirst^^[A-D]}'
echo "${upperFirst}   ${upperFirst^^[A-D]}       AbCdEf      "'${upperFirst^^[A-D]}'

echo "${lowerFirst}   ${lowerFirst^^[a-d]}       ABCDeF      "'${lowerFirst^^[a-d]}'
echo "${upperFirst}   ${upperFirst^^[a-d]}       ABCDEf      "'${upperFirst^^[a-d]}'

This gives:

lowerFirst=aBcDeF
upperFirst=AbCdEf

Original Converted    Expected    Conversion
aBcDeF   aBcDeF       abcDeF      ${lowerFirst,[A-D]}
AbCdEf   abCdEf       abCdEf      ${upperFirst,[A-D]}
aBcDeF   aBcDeF       aBcDeF      ${lowerFirst,[a-d]}
AbCdEf   AbCdEf       AbCdEf      ${upperFirst,[a-d]}
aBcDeF   aBcDeF       aBcDeF      ${lowerFirst^[A-D]}
AbCdEf   AbCdEf       AbCdEf      ${upperFirst^[A-D]}
aBcDeF   ABcDeF       abcDeF      ${lowerFirst^[a-d]}
AbCdEf   AbCdEf       abCdEf      ${upperFirst^[a-d]}

aBcDeF   abcdeF       abcdeF      ${lowerFirst,,[A-D]}
AbCdEf   abcdEf       abcdEf      ${upperFirst,,[A-D]}
aBcDeF   aBcDeF       aBcDeF      ${lowerFirst,,[a-d]}
AbCdEf   AbCdEf       AbCdEf      ${upperFirst,,[a-d]}
aBcDeF   aBcDeF       aBcDeF      ${lowerFirst^^[A-D]}
AbCdEf   AbCdEf       AbCdEf      ${upperFirst^^[A-D]}
aBcDeF   ABCDeF       ABCDeF      ${lowerFirst^^[a-d]}
AbCdEf   ABCDEf       ABCDEf      ${upperFirst^^[a-d]}

If I ask for the bash version, I get:

$ bash --version
GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)

Am I interpreting this correctly? If not, what am I getting wrong?


Solution

  • The bash manual states:

    The ^^ and ,, expansions convert each matched character in the expanded value; the ^ and , expansions match and convert only the first character in the expanded value.

    , will convert the first character to lowercase if it matches the pattern. ,, will convert all matching characters. There is no way to convert only the first matching character.