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


Answers:


2179

有多种方式:

POSIX标准

TR

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

AWK

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

非POSIX

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

重击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

佩尔

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

重击

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“]]奇怪地匹配[bz]内的情况,但[AZ]错误地匹配了[bz]。Bash被双负数(“ uncasematch”)弄糊涂了!:-)


9
我是否缺少某些内容,或者您​​的上一个示例(在Bash中)实际上做了完全不同的事情?它适用于“ ABX”,但是如果您word="Hi All"像其他示例一样进行操作,则返回ha,而不是hi all。它仅适用于大写字母,并跳过已经小写的字母。
jangosteve

26
请注意,在POSIX标准中仅指定trawk示例。
理查德·汉森

178
tr '[:upper:]' '[:lower:]'将使用当前语言环境来确定大写/小写字母的等效项,因此它将与使用带有变音符号的字母的语言环境一起使用。
理查德·汉森

10
如何将输出转换为新变量?即说我想将小写的字符串转换为新变量?
亚当·帕金

60
@亚当:b="$(echo $a | tr '[A-Z]' '[a-z]')"
蒂诺

434

在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选项更改变量的属性,而不是内容。我的示例中的重新分配更新了内容以显示更改。

编辑:

${var~}根据ghostdog74的建议,添加了“按单词切换第一个字符”()。

编辑:更正了波浪号行为,以匹配Bash 4.3。


5
相当奇怪的“ ^^”和“,”运算符不适用于非ASCII字符,但是“ ~~”可以...因此string="łódź"; echo ${string~~}将返回“ŁÓDŹ”,但echo ${string^^}返回“łóDź”。即使在LC_ALL=pl_PL.utf-8。那是使用bash 4.2.24。
休伯特·卡里奥

2
@HubertKario:太奇怪了。在Bash 4.0.33中,对于我来说是相同的,在中具有相同的字符串en_US.UTF-8。这是一个错误,我已经报告了。
暂停,直到另行通知。

1
@HubertKario:试试看echo "$string" | tr '[:lower:]' '[:upper:]'。它可能会显示相同的故障。因此,问题至少部分不是Bash的问题。
暂停,直到另行通知。

1
@DennisWilliamson:是的,我也注意到了(请参阅Shuvalov回答的评论)。我只是说,“这些东西仅用于ASCII”,但是它确实是“ ~~”运算符,所以它不像代码和转换表还不存在……
Hubert Kario

4
@HubertKario:Bash维护者已确认该错误,并表示它将在下一版本中修复。
暂停,直到另行通知。

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

4
@RichardHansen:tr不适用于非ACII字符。我确实有正确的语言环境设置和生成的语言环境文件。有什么想法我会做错什么吗?
休伯特·卡里奥

仅供参考:这适用于Windows / Msys。其他一些建议则没有。
wasatchwizard

3
为什么[:upper:]需要?
mgutt

77

tr

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

AWK

{ print tolower($0) }

sed

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/

2
+1 a="$(tr [A-Z] [a-z] <<< "$a")"对我来说似乎最简单。我仍然是初学者...
Sandeepan Nath 2011年

2
我强烈推荐sed解决方案;我一直在一个由于某种原因而没有的环境中工作,tr但是我还没有找到一个没有的系统sed,加上很多时间我想这样做,sed所以无论如何我还是做了其他事情,因此可以命令一起成为一个(长)语句。
Haravikk

2
方括号表达式应加引号。在中tr [A-Z] [a-z] A,如果存在由单个字母或nullgob组成的文件名,则Shell可以执行文件名扩展。tr "[A-Z]" "[a-z]" A将表现正常。
丹尼斯

2
@CamiloMartin这是一个BusyBox系统,我遇到了这个问题,特别是Synology NASes,但是我也在其他一些系统上遇到过。最近,我一直在做很多跨平台的shell脚本编写,并且要求不安装任何额外的东西,这会使事情变得非常棘手!但是,我还没有遇到没有系统的情况sed
Haravikk 2014年

2
请注意,tr [A-Z] [a-z]在几乎所有语言环境中这都是不正确的。例如,在en-US语言环境中,A-Z实际上是interval AaBbCcDdEeFfGgHh...XxYyZ
fuz

44

我知道这是一篇过时的文章,但是我在另一个网站上做了这个答案,所以我认为应该在这里发布:

上->下:使用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) }'`

或Sed:

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

或重击4:

b=${a,,}

或NodeJS(如果有)(有点发疯...):

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

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

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

下->上

使用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) }'`

或Sed:

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

或重击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

大写。


@JESii都对我有用-上->下和下->上。我在64位Debian Stretch上使用sed 4.2.2和Bash 4.3.42(1)。
nettux

1
嗨,@ nettux443 ...我只是再次尝试了bash操作,但它仍然对我失败,并显示错误消息“坏替换”。我在OSX上使用自制软件的bash:GNU bash,版本4.3.42(1)
发行

5
不使用!所有生成脚本的示例都非常脆弱。如果的值a包含单引号,则不仅行为不当,而且存在严重的安全问题。
三人

我最喜欢sed解决方案,因为sed总是无处不在。
Dudi Boy

我更喜欢使用dd解决方案。请注意,您需要扎根才能正常工作
inetphantom


18

使用GNU sed

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

例:

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

12

前Bash 4.0

Bash减小字符串的大小写并分配给变量

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

echo "$VARIABLE"

5
无需echo管道:使用$(tr '[:upper:]' '[:lower:]' <<<"$VARIABLE")
Tino

3
@Tino这里的字符串也不能移植回真正的旧版本的Bash;我相信它是在v3中引入的。
三胞胎

1
@tripleee是的,它是在bash-2.05b中引入的-但是,这是我在系统上可以找到的最古老的bash
Tino

11

对于仅使用内置函数的标准外壳程序(没有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}"
}

我想知道您是否在此脚本中不放任重任,因为它不能在FreeBSD上移植:$ {1:$ ...}:替代错误
Dereckson 2014年

2
确实; 的子串${var:1:1}是Bashism。
2015年

这种方法的性能指标非常差。请参阅我的答案以获取指标。
Dejay Clayton '18

9

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

例:

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


7

正则表达式

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

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

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

我希望该解决方案证明是有用的,该命令不做的一件事就是用下划线替换空格-哦,也许还有一次。


尽管看起来不错,但无论出于何种原因,这对我都不起作用。不过,我确实将此作为替代方法来工作:find。-exec / bin / bash -c'mv {}`tr [AZ] [az] <<< {}`'\;
John Rix 2013年

这需要prename来自perldpkg -S "$(readlink -e /usr/bin/rename)"perl: /usr/bin/prename
Tino 2015年

4

许多答案是使用外部程序,实际上并没有使用 Bash

如果您知道可以使用Bash4,则应该只使用 ${VAR,,}符号(既简单又酷)。对于4之前的Bash(例如,我的Mac仍使用Bash 3.2)。我使用了@ ghostdog74的答案的更正版本来创建更可移植的版本。

您可以致电lowercase 'my STRING'并获得小写版本。我读了有关将结果设置为var的注释,但这并不是真正可移植的Bash,因为我们无法返回字符串。打印是最好的解决方案。容易被类似的东西捕获var="$(lowercase $str)"

如何运作

这种方法的工作方式是,先获取每个字符的ASCII整数表示形式,printf然后使用adding 32if upper-to->lowersubtracting 32if 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选项。


4

仅对字母进行大小写转换。因此,这应该工作整齐。

我专注于将az的字母从大写转换为小写。其他任何字符都应该按原样打印在标准输出中...

将z范围内path / to / file / filename中的所有文本转换为AZ

用于将小写转换为大写

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

3

如果使用v4,则已烘焙。如果没有,这是一个简单的,广泛适用的解决方案。此线程上的其他答案(和注释)对于创建下面的代码很有帮助。

# 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函数中,即使字符串带有引号,使用using ${!1//\'/"'\''"}代替也${!1}可以使之工作。

3

对于低于4.0的Bash版本,此版本应该是最快的(因为它不会派生/执行任何命令):

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

尽管技工龙的答案确实适合我,但它也有潜力。


不错!有关此方法性能的分析,请参阅我的指标指标。
Dejay Clayton

3

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

  1. GNU bash版本4.1.2(1)-发行版(x86_64-redhat-linux-gnu)
  2. GNU bash版本3.2.57(1)-发行版(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风格的for循环遍历字符串。对于下面的行,如果您在我学到这之前没有看到 过这样的内容。在这种情况下,该行检查输入中是否存在char $ {input:$ i:1}(小写),如果存在,将其替换为给定的char $ {ucs:$ j:1}(大写)并存储它返回输入。

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

这是非常低效的,在上面的示例中循环了650次,花了35秒在我的计算机上执行了1000次调用。对于仅循环11次且只需不到5秒即可执行1000次调用的替代方法,请参阅我的替代答案。
Dejay Clayton '18年

1
谢谢,尽管仅从它来看应该很明显。页面错误可能是由于输入大小和您正在执行的迭代次数引起的。不过,我喜欢您的解决方案。
JaredTS486

3

这是JaredTS486使用本地Bash功能(包括低于4.0的Bash版本)来优化其方法的方法的更快变化。

我已经为小字符串(25个字符)和大字符串(445个字符)定时了1000次此方法的迭代,无论是小写还是大写转换。由于测试字符串主要是小写字母,因此转换为小写字母通常比转换为大写字母更快。

我已经将我的方法与本页上其他与Bash 3.2兼容的答案进行了比较。我的方法比此处记录的大多数方法具有更高的性能,甚至比tr在几种情况下还快。

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

  • 我的小写字母为0.46s;大写为0.96s
  • Orwellophile 小写方法1.16分;大写1.59s
  • tr小写字母为3.67秒;大写3.81秒
  • ghostdog74 小写方式11.12秒;大写31.41秒
  • Technosaurus 处理小写字母的时间为26.25s;大写26.21秒
  • JaredTS486 小写方法25.06秒;大写27.04秒

445个字符的1,000次迭代的计时结果(由Witter Bynner的诗歌“ The Robin”组成):

  • 我的小写方法为2秒;大写字母12s
  • 4s为tr小写;大写字母4s
  • Orwellophile的小写方法为20 ;大写字母29s
  • ghostdog74 小写方式为75s;大写为669s。有趣的是,在比赛占优势的测试与失误占优势的测试之间,性能差异有多大
  • techsausaurus的小写字母为467秒;大写449s
  • JaredTS486 小写方法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. 使用针对性能进行编译和优化的外壳程序机制,例如变量内的全局字符串替换,变量后缀修整以及正则表达式搜索和匹配。这些机制比通过字符串手动迭代要快得多
  4. 仅循环计数要转换的唯一匹配字符数所需的次数。例如,将具有三个不同大写字符的字符串转换为小写仅需要3次循环迭代。对于预配置的ASCII字母,最大循环迭代次数为26
  5. UCSLCS可以增加其他字符

2

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

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

1

简单的方法

echo "Hi all" | awk '{ print tolower($0); }'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.