如何在Bash中将字符串转换为小写?

问题描述 投票:1062回答:20

中有没有办法将字符串转换为小写字符串?

例如,如果我有:

a="Hi all"

我想将其转换为:

"hi all"
string bash shell uppercase lowercase
20个回答
1898
投票

各种方式:

POSIX standard

tr

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

AWK

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

非POSIX

您可能会遇到以下示例遇到的可移植性问题:

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

注意:YMMV就是这个。对我来说不起作用(GNU bash版本4.2.46和4.0.33(和相同的行为2.05b.0但没有实现nocasematch))即使使用shopt -u nocasematch;也是如此。取消设置nocasematch导致[[“fooBaR”==“FOObar”]]匹配OK BUT内部奇怪地[b-z]被[A-Z]错误地匹配。 Bash被双阴性(“未设置的nocasematch”)弄糊涂了! :-)


8
投票

在bash 4中,您可以使用排版

例:

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

7
投票

正则表达式

我想赞扬我希望分享的命令,但事实是我从http://commandlinefu.com获得了我自己使用的命令。它的优点是,如果你cd到你自己的主文件夹中的任何目录,它将递归地将所有文件和文件夹更改为小写,请谨慎使用。这是一个出色的命令行修复,对于存储在驱动器上的大量专辑特别有用。

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

您可以在find之后指定一个目录来代替点(。),表示当前目录或完整路径。

我希望这个解决方案证明是有用的,这个命令没有做的一件事是用下划线替换空格 - 哦,或许另一次。


7
投票

你可以试试这个

s="Hello World!" 

echo $s  # Hello World!

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

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

enter image description here

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


3
投票

如果使用v4,这是baked-in。如果没有,这是一个简单,广泛适用的解决方案。此线程上的其他答案(和注释)在创建下面的代码时非常有用。

# 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:] )\'
}

笔记:

  • 做:a="Hi All"然后:lcase a将做同样的事情:a=$( echolcase "Hi All" )
  • 在lcase函数中,使用${!1//\'/"'\''"}而不是${!1},即使字符串有引号也可以使用它。

3
投票

对于早于4.0的Bash版本,此版本应该是最快的(因为它不是fork/exec任何命令):

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也有潜力,虽然它确实适合我。


3
投票

尽管这个问题有多久,与this answer by technosaurus相似。我很难找到一个可以在大多数平台(我使用的)以及旧版本的bash上移植的解决方案。我也对数组,函数以及打印,回声和临时文件的使用感到沮丧,以检索琐碎的变量。到目前为止,这对我来说非常有效,我想我会分享。我的主要测试环境是:

  1. GNU bash,版本4.1.2(1)-release(x86_64-redhat-linux-gnu)
  2. GNU bash,版本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

简单的C-style for loop迭代字符串。对于下面的行,如果你在this is where I learned this之前没有看到这样的东西。在这种情况下,该行检查输入中是否存在char $ {input:$ i:1}(小写),如果是,则用给定的char $ {ucs:$ j:1}(大写)替换它并存储它回到输入。

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

3
投票

许多答案使用外部程序,这不是真正使用Bash

如果你知道你将有Bash4可用,你应该真的只使用${VAR,,}表示法(它很简单,很酷)。对于4之前的Bash(我的Mac仍然使用Bash 3.2)。我使用@ ghostdog74的修正版本的答案来创建一个更便携的版本。

你可以打电话给lowercase 'my STRING'并获得一个小写版本。我读到了关于将结果设置为var的注释,但这在Bash中并不是真正可移植的,因为我们无法返回字符串。打印它是最好的解决方案。像var="$(lowercase $str)"这样容易捕获。

这是如何工作的

这种方法的工作方式是使用printf获取每个char的ASCII整数表示,然后使用adding 32获取upper-to->lower,如果subtracting 32获取lower-to->upper。然后再次使用printf将数字转换回char。从'A' -to-> 'a'我们有32个字符的差异。

使用printf解释:

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

97 - 65 = 32

这是带有示例的工作版本。 请注意代码中的注释,因为它们解释了很多内容:

#!/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

运行此后的结果:

$ ./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!

这应该仅适用于ASCII字符。

对我来说这很好,因为我知道我只会传递ASCII字符。 例如,我将此用于一些不区分大小写的CLI选项。


2
投票

转换大小写仅适用于字母。所以,这应该整齐地工作。

我专注于将a-z之间的字母表从大写转换为小写。任何其他字符应该只是在stdout中打印,因为它是......

将a / z范围内path / to / file / filename中的所有文本转换为A-Z

用于将小写字母转换为大写字母

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

用于从大写转换为小写

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

例如,

文件名:

my name is xyz

转换为:

MY NAME IS XYZ

例2:

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

例3:

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

2
投票

如果您喜欢python并且可以选择安装新的python包,那么您可以试试这个python utility

# install pythonp
$ pip install pythonp

$ echo $a | pythonp "l.lower()"

1
投票

这是使用本机Bash功能(包括Bash版本<4.0)来优化其方法的JaredTS486's approach的更快变化。

我为这个小方法(25个字符)和一个更大的字符串(445个字符)计算了1000次这种方法的迭代,包括小写和大写转换。由于测试字符串主要是小写的,因此转换为小写通常比大写更快。

我已将此方法与此页面上与Bash 3.2兼容的其他几个答案进行了比较。我的方法比这里记录的大多数方法更有效,并且在几种情况下甚至比tr更快。

以下是包含25个字符的1,000次迭代的计时结果:

定时结果为445个字符的1000次迭代(由Witter Bynner的诗“The Robin”组成):

  • 我接近小写的2s; 12s为大写
  • 4s为tr为小写; 4s为大写
  • 20秒为Orwellophile's approach为小写; 29s表示大写
  • 75秒为ghostdog74's接近小写; 669s为大写。值得注意的是,在具有主要匹配的测试与具有主要未命中的测试之间,性能差异是多么显着
  • technosaurus' approach的467s改为小写;大写449s
  • JaredTS486's approach的660s为小写; 660s为大写。有趣的是,这种方法在Bash中产生了连续的页面错误(内存交换)

解:

#!/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}"
}

方法很简单:输入字符串中存在任何剩余的大写字母,找到下一个字母,并用该小写字母替换该字母的所有实例。重复直到更换所有大写字母。

我的解决方案的一些性能特征:

  1. 仅使用shell内置实用程序,这可以避免在新进程中调用外部二进制实用程序的开销
  2. 避免子壳,这会导致性能损失
  3. 使用针对性能进行编译和优化的shell机制,例如变量中的全局字符串替换,变量后缀修剪以及正则表达式搜索和匹配。这些机制比通过字符串手动迭代要快得多
  4. 仅循环要转换的唯一匹配字符数所需的次数。例如,将具有三个不同大写字符的字符串转换为小写只需要3次循环迭代。对于预配置的ASCII字母表,循环迭代的最大数量为26
  5. UCSLCS可以增加额外的字符

398
投票

在Bash 4中:

小写

$ 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

大写

$ 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

切换(未记录,但可选择在编译时配置)

$ 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

大写(未记录,但可选择在编译时配置)

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

标题案例:

$ 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

要关闭declare属性,请使用+。例如,declare +c string。这会影响后续分配,而不会影响当前值。

declare选项更改变量的属性,但不更改内容。我的示例中的重新分配更新了内容以显示更改。

编辑:

按照ghostdog74的建议添加了“逐字逐句切换”(${var~})。

编辑:更正了波形符合行为以匹配Bash 4.3。


0
投票

将转换后的字符串存储到变量中。以下为我工作 - $SOURCE_NAME$TARGET_NAME

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

118
投票
echo "Hi All" | tr "[:upper:]" "[:lower:]"

74
投票

tr:

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

AWK:

{ print tolower($0) }

sed:

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/

41
投票

我知道这是一个古老的帖子,但我为另一个网站做了这个答案所以我想我会在这里发布:

UPPER - > lower:使用python:

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

或Ruby:

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

或者Perl(可能是我最喜欢的):

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

或PHP:

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

或者awk:

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

不过还是:

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

或者Bash 4:

b=${a,,}

或NodeJS,如果你有它(并且有点疯狂......):

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

你也可以使用dd(但我不会!):

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

降低 - > UPPER:

使用python:

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

或Ruby:

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

或者Perl(可能是我最喜欢的):

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

或PHP:

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

或者awk:

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

不过还是:

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

或者Bash 4:

b=${a^^}

或NodeJS,如果你有它(并且有点疯狂......):

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

你也可以使用dd(但我不会!):

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

此外,当你说'壳'我假设你的意思是bash,但如果你可以使用zsh它就像

b=$a:l

小写和

b=$a:u

对于大写。


28
投票

在zsh中:

echo $a:u

得爱zsh!


17
投票

使用GNU sed

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

例:

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

11
投票

对于仅使用内置函数的标准shell(无bashisms):

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}"
}

对于大写:

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}"
}

11
投票

对于Bash 4.0

Bash降低字符串的大小写并赋值给变量

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

echo "$VARIABLE"
© www.soinside.com 2019 - 2024. All rights reserved.