How to convert a string to lower case in Bash?

2018-12-31 19:38发布

问题:

Is there a way in bash to convert a string into a lower case string?

For example, if I have:

a=\"Hi all\"

I want to convert it to:

\"hi all\"

回答1:

The are various ways:

POSIX standard

tr

$ echo \"$a\" | tr \'[:upper:]\' \'[:lower:]\'
hi all

AWK

$ echo \"$a\" | awk \'{print tolower($0)}\'
hi all

Non-POSIX

You may run into portability issues with the following examples:

Bash 4.0

$ echo \"${a,,}\"
hi all

sed

$ echo \"$a\" | sed -e \'s/\\(.*\\)/\\L\\1/\'
hi all
# this also works:
$ sed -e \'s/\\(.*\\)/\\L\\1/\' <<< \"$a\"
hi all

Perl

$ echo \"$a\" | perl -ne \'print lc\'
hi all

Bash

lc(){
    case \"$1\" in
        [A-Z])
        n=$(printf \"%d\" \"\'$1\")
        n=$((n+32))
        printf \\\\$(printf \"%o\" \"$n\")
        ;;
        *)
        printf \"%s\" \"$1\"
        ;;
    esac
}
word=\"I Love Bash\"
for((i=0;i<${#word};i++))
do
    ch=\"${word:$i:1}\"
    lc \"$ch\"
done


回答2:

In Bash 4:

To lowercase

$ string=\"A FEW WORDS\"
$ echo \"${string,}\"
a FEW WORDS
$ echo \"${string,,}\"
a few words
$ echo \"${string,,[AEIUO]}\"
a FeW WoRDS

$ string=\"A Few Words\"
$ declare -l string
$ string=$string; echo \"$string\"
a few words

To uppercase

$ string=\"a few words\"
$ echo \"${string^}\"
A few words
$ echo \"${string^^}\"
A FEW WORDS
$ echo \"${string^^[aeiou]}\"
A fEw wOrds

$ string=\"A Few Words\"
$ declare -u string
$ string=$string; echo \"$string\"
A FEW WORDS

Toggle (undocumented, but optionally configurable at compile time)

$ string=\"A Few Words\"
$ echo \"${string~~}\"
a fEW wORDS
$ string=\"A FEW WORDS\"
$ echo \"${string~}\"
a FEW WORDS
$ string=\"a few words\"
$ echo \"${string~}\"
A few words

Capitalize (undocumented, but optionally configurable at compile time)

$ string=\"a few words\"
$ declare -c string
$ string=$string
$ echo \"$string\"
A few words

Title case:

$ string=\"a few words\"
$ string=($string)
$ string=\"${string[@]^}\"
$ echo \"$string\"
A Few Words

$ declare -c string
$ string=(a few words)
$ echo \"${string[@]}\"
A Few Words

$ string=\"a FeW WOrdS\"
$ string=${string,,}
$ string=${string~}
$ echo \"$string\"
A few words

To turn off a declare attribute, use +. For example, declare +c string. This affects subsequent assignments and not the current value.

The declare options change the attribute of the variable, but not the contents. The reassignments in my examples update the contents to show the changes.

Edit:

Added \"toggle first character by word\" (${var~}) as suggested by ghostdog74.

Edit: Corrected tilde behavior to match Bash 4.3.



回答3:

echo \"Hi All\" | tr \"[:upper:]\" \"[:lower:]\"


回答4:

tr:

a=\"$(tr [A-Z] [a-z] <<< \"$a\")\"

AWK:

{ print tolower($0) }

sed:

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/


回答5:

I know this is an oldish post but I made this answer for another site so I thought I\'d post it up here:

UPPER -> lower: use python:

b=`echo \"print \'$a\'.lower()\" | python`

Or Ruby:

b=`echo \"print \'$a\'.downcase\" | ruby`

Or Perl (probably my favorite):

b=`perl -e \"print lc(\'$a\');\"`

Or PHP:

b=`php -r \"print strtolower(\'$a\');\"`

Or Awk:

b=`echo \"$a\" | awk \'{ print tolower($1) }\'`

Or Sed:

b=`echo \"$a\" | sed \'s/./\\L&/g\'`

Or Bash 4:

b=${a,,}

Or NodeJS if you have it (and are a bit nuts...):

b=`echo \"console.log(\'$a\'.toLowerCase());\" | node`

You could also use dd (but I wouldn\'t!):

b=`echo \"$a\" | dd  conv=lcase 2> /dev/null`

lower -> UPPER:

use python:

b=`echo \"print \'$a\'.upper()\" | python`

Or Ruby:

b=`echo \"print \'$a\'.upcase\" | ruby`

Or Perl (probably my favorite):

b=`perl -e \"print uc(\'$a\');\"`

Or PHP:

b=`php -r \"print strtoupper(\'$a\');\"`

Or Awk:

b=`echo \"$a\" | awk \'{ print toupper($1) }\'`

Or Sed:

b=`echo \"$a\" | sed \'s/./\\U&/g\'`

Or Bash 4:

b=${a^^}

Or NodeJS if you have it (and are a bit nuts...):

b=`echo \"console.log(\'$a\'.toUpperCase());\" | node`

You could also use dd (but I wouldn\'t!):

b=`echo \"$a\" | dd  conv=ucase 2> /dev/null`

Also when you say \'shell\' I\'m assuming you mean bash but if you can use zsh it\'s as easy as

b=$a:l

for lower case and

b=$a:u

for upper case.



回答6:

In zsh:

echo $a:u

Gotta love zsh!



回答7:

Using GNU sed:

sed \'s/.*/\\L&/\'

Example:

$ foo=\"Some STRIng\";
$ foo=$(echo \"$foo\" | sed \'s/.*/\\L&/\')
$ echo \"$foo\"
some string


回答8:

For a standard shell (without bashisms) using only builtins:

uppers=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lowers=abcdefghijklmnopqrstuvwxyz

lc(){ #usage: lc \"SOME STRING\" -> \"some string\"
    i=0
    while ([ $i -lt ${#1} ]) do
        CUR=${1:$i:1}
        case $uppers in
            *$CUR*)CUR=${uppers%$CUR*};OUTPUT=\"${OUTPUT}${lowers:${#CUR}:1}\";;
            *)OUTPUT=\"${OUTPUT}$CUR\";;
        esac
        i=$((i+1))
    done
    echo \"${OUTPUT}\"
}

And for upper case:

uc(){ #usage: uc \"some string\" -> \"SOME STRING\"
    i=0
    while ([ $i -lt ${#1} ]) do
        CUR=${1:$i:1}
        case $lowers in
            *$CUR*)CUR=${lowers%$CUR*};OUTPUT=\"${OUTPUT}${uppers:${#CUR}:1}\";;
            *)OUTPUT=\"${OUTPUT}$CUR\";;
        esac
        i=$((i+1))
    done
    echo \"${OUTPUT}\"
}


回答9:

Pre Bash 4.0

Bash Lower the Case of a string and assign to variable

VARIABLE=$(echo \"$VARIABLE\" | tr \'[:upper:]\' \'[:lower:]\') 

echo \"$VARIABLE\"


回答10:

Regular expression

I would like to take credit for the command I wish to share but the truth is I obtained it for my own use from http://commandlinefu.com. It has the advantage that if you cd to any directory within your own home folder that is it will change all files and folders to lower case recursively please use with caution. It is a brilliant command line fix and especially useful for those multitudes of albums you have stored on your drive.

find . -depth -exec rename \'s/(.*)\\/([^\\/]*)/$1\\/\\L$2/\' {} \\;

You can specify a directory in place of the dot(.) after the find which denotes current directory or full path.

I hope this solution proves useful the one thing this command does not do is replace spaces with underscores - oh well another time perhaps.



回答11:

In bash 4 you can use typeset

Example:

A=\"HELLO WORLD\"
typeset -l A=$A


回答12:

You can try this

s=\"Hello World!\" 

echo $s  # Hello World!

a=${s,,}
echo $a  # hello world!

b=${s^^}
echo $b  # HELLO WORLD!

\"enter

ref : http://wiki.workassis.com/shell-script-convert-text-to-lowercase-and-uppercase/



回答13:

If using v4, this is baked-in. If not, here is a simple, widely applicable solution. Other answers (and comments) on this thread were quite helpful in creating the code below.

# Like echo, but converts to lowercase
echolcase () {
    tr [:upper:] [:lower:] <<< \"${*}\"
}

# Takes one arg by reference (var name) and makes it lowercase
lcase () { 
    eval \"${1}\"=\\\'$(echo ${!1//\\\'/\"\'\\\'\'\"} | tr [:upper:] [:lower:] )\\\'
}

Notes:

  • Doing: a=\"Hi All\" and then: lcase a will do the same thing as: a=$( echolcase \"Hi All\" )
  • In the lcase function, using ${!1//\\\'/\"\'\\\'\'\"} instead of ${!1} allows this to work even when the string has quotes.


回答14:

For Bash versions earlier than 4.0, this version should be fastest (as it doesn\'t fork/exec any commands):

function string.monolithic.tolower
{
   local __word=$1
   local __len=${#__word}
   local __char
   local __octal
   local __decimal
   local __result

   for (( i=0; i<__len; i++ ))
   do
      __char=${__word:$i:1}
      case \"$__char\" in
         [A-Z] )
            printf -v __decimal \'%d\' \"\'$__char\"
            printf -v __octal \'%03o\' $(( $__decimal ^ 0x20 ))
            printf -v __char \\\\$__octal
            ;;
      esac
      __result+=\"$__char\"
   done
   REPLY=\"$__result\"
}

technosaurus\'s answer had potential too, although it did run properly for mee.



回答15:

In spite of how old this question is and similar to this answer by technosaurus. I had a hard time finding a solution that was portable across most platforms (That I Use) as well as older versions of bash. I have also been frustrated with arrays, functions and use of prints, echos and temporary files to retrieve trivial variables. This works very well for me so far I thought I would share. My main testing environments are:

  1. GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
  2. GNU bash, version 3.2.57(1)-release (sparc-sun-solaris2.10)
lcs=\"abcdefghijklmnopqrstuvwxyz\"
ucs=\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"
input=\"Change Me To All Capitals\"
for (( i=0; i<\"${#input}\"; i++ )) ; do :
    for (( j=0; j<\"${#lcs}\"; j++ )) ; do :
        if [[ \"${input:$i:1}\" == \"${lcs:$j:1}\" ]] ; then
            input=\"${input/${input:$i:1}/${ucs:$j:1}}\" 
        fi
    done
done

Simple C-style for loop to iterate through the strings. For the line below if you have not seen anything like this before this is where I learned this. In this case the line checks if the char ${input:$i:1} (lower case) exists in input and if so replaces it with the given char ${ucs:$j:1} (upper case) and stores it back into input.

input=\"${input/${input:$i:1}/${ucs:$j:1}}\"


回答16:

Many answers using external programs, which is not really using Bash.

If you know you will have Bash4 available you should really just use the ${VAR,,} notation (it is easy and cool). For Bash before 4 (My Mac still uses Bash 3.2 for example). I used the corrected version of @ghostdog74 \'s answer to create a more portable version.

One you can call lowercase \'my STRING\' and get a lowercase version. I read comments about setting the result to a var, but that is not really portable in Bash, since we can\'t return strings. Printing it is the best solution. Easy to capture with something like var=\"$(lowercase $str)\".

How this works

The way this works is by getting the ASCII integer representation of each char with printf and then adding 32 if upper-to->lower, or subtracting 32 if lower-to->upper. Then use printf again to convert the number back to a char. From \'A\' -to-> \'a\' we have a difference of 32 chars.

Using printf to explain:

$ printf \"%d\\n\" \"\'a\"
97
$ printf \"%d\\n\" \"\'A\"
65

97 - 65 = 32

And this is the working version with examples.
Please note the comments in the code, as they explain a lot of stuff:

#!/bin/bash

# lowerupper.sh

# Prints the lowercase version of a char
lowercaseChar(){
    case \"$1\" in
        [A-Z])
            n=$(printf \"%d\" \"\'$1\")
            n=$((n+32))
            printf \\\\$(printf \"%o\" \"$n\")
            ;;
        *)
            printf \"%s\" \"$1\"
            ;;
    esac
}

# Prints the lowercase version of a sequence of strings
lowercase() {
    word=\"$@\"
    for((i=0;i<${#word};i++)); do
        ch=\"${word:$i:1}\"
        lowercaseChar \"$ch\"
    done
}

# Prints the uppercase version of a char
uppercaseChar(){
    case \"$1\" in
        [a-z])
            n=$(printf \"%d\" \"\'$1\")
            n=$((n-32))
            printf \\\\$(printf \"%o\" \"$n\")
            ;;
        *)
            printf \"%s\" \"$1\"
            ;;
    esac
}

# Prints the uppercase version of a sequence of strings
uppercase() {
    word=\"$@\"
    for((i=0;i<${#word};i++)); do
        ch=\"${word:$i:1}\"
        uppercaseChar \"$ch\"
    done
}

# The functions will not add a new line, so use echo or
# append it if you want a new line after printing

# Printing stuff directly
lowercase \"I AM the Walrus!\"$\'\\n\'
uppercase \"I AM the Walrus!\"$\'\\n\'

echo \"----------\"

# Printing a var
str=\"A StRing WITH mixed sTUFF!\"
lowercase \"$str\"$\'\\n\'
uppercase \"$str\"$\'\\n\'

echo \"----------\"

# Not quoting the var should also work, 
# since we use \"$@\" inside the functions
lowercase $str$\'\\n\'
uppercase $str$\'\\n\'

echo \"----------\"

# Assigning to a var
myLowerVar=\"$(lowercase $str)\"
myUpperVar=\"$(uppercase $str)\"
echo \"myLowerVar: $myLowerVar\"
echo \"myUpperVar: $myUpperVar\"

echo \"----------\"

# You can even do stuff like
if [[ \'option 2\' = \"$(lowercase \'OPTION 2\')\" ]]; then
    echo \"Fine! All the same!\"
else
    echo \"Ops! Not the same!\"
fi

exit 0

And the results after running this:

$ ./lowerupper.sh 
i am the walrus!
I AM THE WALRUS!
----------
a string with mixed stuff!
A STRING WITH MIXED STUFF!
----------
a string with mixed stuff!
A STRING WITH MIXED STUFF!
----------
myLowerVar: a string with mixed stuff!
myUpperVar: A STRING WITH MIXED STUFF!
----------
Fine! All the same!

This should only work for ASCII characters though.

For me it is fine, since I know I will only pass ASCII chars to it.
I am using this for some case-insensitive CLI options, for example.



回答17:

Converting case is done for alphabets only. So, this should work neatly.

I am focusing on converting alphabets between a-z from upper case to lower case. Any other characters should just be printed in stdout as it is...

Converts the all text in path/to/file/filename within a-z range to A-Z

For converting lower case to upper case

cat path/to/file/filename | tr \'a-z\' \'A-Z\'

For converting from upper case to lower case

cat path/to/file/filename | tr \'A-Z\' \'a-z\'

For example,

filename:

my name is xyz

gets converted to:

MY NAME IS XYZ

Example 2:

echo \"my name is 123 karthik\" | tr \'a-z\' \'A-Z\'
# Output:
# MY NAME IS 123 KARTHIK

Example 3:

echo \"my name is 123 &&^&& #@$#@%%& kAR2~thik\" | tr \'a-z\' \'A-Z\'
# Output:
# MY NAME IS 123 &&^&& #@0@%%& KAR2~THIK


回答18:

To store the transformed string into a variable. Following worked for me - $SOURCE_NAME to $TARGET_NAME

TARGET_NAME=\"`echo $SOURCE_NAME | tr \'[:upper:]\' \'[:lower:]\'`\"


回答19:

This is a far faster variation of JaredTS486\'s approach that uses native Bash capabilities (including Bash versions <4.0) to optimize his approach.

I\'ve timed 1,000 iterations of this approach for a small string (25 characters) and a larger string (445 characters), both for lowercase and uppercase conversions. Since the test strings are predominantly lowercase, conversions to lowercase are generally faster than to uppercase.

I\'ve compared my approach with several other answers on this page that are compatible with Bash 3.2. My approach is far more performant than most approaches documented here, and is even faster than tr in several cases.

Here are the timing results for 1,000 iterations of 25 characters:

  • 0.46s for my approach to lowercase; 0.96s for uppercase
  • 1.16s for Orwellophile\'s approach to lowercase; 1.59s for uppercase
  • 3.67s for tr to lowercase; 3.81s for uppercase
  • 11.12s for ghostdog74\'s approach to lowercase; 31.41s for uppercase
  • 26.25s for technosaurus\' approach to lowercase; 26.21s for uppercase
  • 25.06s for JaredTS486\'s approach to lowercase; 27.04s for uppercase

Timing results for 1,000 iterations of 445 characters (consisting of the poem \"The Robin\" by Witter Bynner):

  • 2s for my approach to lowercase; 12s for uppercase
  • 4s for tr to lowercase; 4s for uppercase
  • 20s for Orwellophile\'s approach to lowercase; 29s for uppercase
  • 75s for ghostdog74\'s approach to lowercase; 669s for uppercase. It\'s interesting to note how dramatic the performance difference is between a test with predominant matches vs. a test with predominant misses
  • 467s for technosaurus\' approach to lowercase; 449s for uppercase
  • 660s for JaredTS486\'s approach to lowercase; 660s for uppercase. It\'s interesting to note that this approach generated continuous page faults (memory swapping) in Bash

Solution:

#!/bin/bash
set -e
set -u

declare LCS=\"abcdefghijklmnopqrstuvwxyz\"
declare UCS=\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"

function lcase()
{
  local TARGET=\"${1-}\"
  local UCHAR=\'\'
  local UOFFSET=\'\'

  while [[ \"${TARGET}\" =~ ([A-Z]) ]]
  do
    UCHAR=\"${BASH_REMATCH[1]}\"
    UOFFSET=\"${UCS%%${UCHAR}*}\"
    TARGET=\"${TARGET//${UCHAR}/${LCS:${#UOFFSET}:1}}\"
  done

  echo -n \"${TARGET}\"
}

function ucase()
{
  local TARGET=\"${1-}\"
  local LCHAR=\'\'
  local LOFFSET=\'\'

  while [[ \"${TARGET}\" =~ ([a-z]) ]]
  do
    LCHAR=\"${BASH_REMATCH[1]}\"
    LOFFSET=\"${LCS%%${LCHAR}*}\"
    TARGET=\"${TARGET//${LCHAR}/${UCS:${#LOFFSET}:1}}\"
  done

  echo -n \"${TARGET}\"
}

The approach is simple: while the input string has any remaining uppercase letters present, find the next one, and replace all instances of that letter with its lowercase variant. Repeat until all uppercase letters are replaced.

Some performance characteristics of my solution:

  1. Uses only shell builtin utilities, which avoids the overhead of invoking external binary utilities in a new process
  2. Avoids sub-shells, which incur performance penalties
  3. Uses shell mechanisms that are compiled and optimized for performance, such as global string replacement within variables, variable suffix trimming, and regex searching and matching. These mechanisms are far faster than iterating manually through strings
  4. Loops only the number of times required by the count of unique matching characters to be converted. For example, converting a string that has three different uppercase characters to lowercase requires only 3 loop iterations. For the preconfigured ASCII alphabet, the maximum number of loop iterations is 26
  5. UCS and LCS can be augmented with additional characters