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

1766

中,有没有一种方法可以将字符串转换为小写字符串?

例如,如果我有:

a="Hi all"

我想将它转换为:

"hi all"

3
请参阅:https://dev59.com/XGgu5IYBdhLWcg3wWVyF - dreftymac
29个回答

14
[dev@localhost ~]$ TEST=STRESS2
[dev@localhost ~]$ echo ${TEST,,}
stress2

13

简单的方式

echo "Hi all" | awk '{ print tolower($0); }'

最好使用gawk,它可以正确处理UTF8编码的字符(以及不同的语言字符集)。如果用'Awk tolower'来处理"ЛШТШФУМ АЩЬФ"这样的字符串,会失败。 - Vit
在 macOS 11.6 上可用的 awk 完美运行:echo 'Đêm lưu trú năm nay' | awk '{ print tolower($0); }' => đêm lưu trú năm nay,以及 echo 'ЛШТШФУМ АЩЬФ' | awk '{ print tolower($0); }' => лштшфум ащьф - Walter Tross

13

1
太好了!我正要写这样的答案。很多答案都添加了许多不必要的信息。 - yosefrow

12

对于仅使用内置命令的标准shell(不含bash特性):

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

我想知道你是否在这个脚本中使用了一些bashism,因为它在FreeBSD sh上不可移植:${1:$...}:错误的替换。 - Dereckson
2
确实,${var:1:1} 的子字符串是 Bashism。 - tripleee
这种方法的性能指标相当糟糕。请查看我的答案以获取指标。 - Dejay Clayton

12
在Bash 4中,您可以使用typeset命令。
示例:
A="HELLO WORLD"
typeset -l A=$A

1
啊,我们这些可怜的macOS用户,在2020年,苹果已经停止支持“bash”,它被困在3.2.57(1)版本中...(注意:是的,我知道我们总是可以从“homebrew”安装更新的“bash”...) - Gwyneth Llewelyn

9

来自 bash manpage:

${parameter^pattern}

${parameter^^pattern}

${parameter,pattern}

${parameter,,pattern}

大小写修改。此扩展会修改parameter中字母字符的大小写。与路径名扩展一样,pattern也会被展开成一个模式。对于parameter展开后的每个字符,都会与pattern进行匹配,如果匹配成功,则改变其大小写。pattern不应尝试匹配多个字符。符号^将匹配pattern的小写字母转换为大写字母;符号,将匹配pattern的大写字母转换为小写字母。符号^^,,会将展开后的值中的每个匹配字符转换,符号^,仅匹配并转换展开后值中的第一个字符。如果省略了pattern,则像?一样处理,可以匹配任何字符。如果parameter@*,则逐个应用大小写修改操作到每个位置参数,并将扩展结果作为结果列表。如果parameter是用@*下标的数组变量,则逐个对数组成员应用大小写修改操作,扩展结果为所得列表。


7

正则表达式

我想分享一个命令,但实际上是从http://commandlinefu.com获取并用于自己的。它的优点是,如果您cd到您自己home文件夹中的任何目录,它将递归地将所有文件和文件夹更改为小写,请谨慎使用。这是一个很棒的命令行修复工具,特别适用于您存储在驱动器上的众多专辑。

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

你可以在find之后指定一个目录来代替点(.),点表示当前目录或完整路径。
我希望这个解决方案能够有所帮助,但是这个命令不能将空格替换为下划线——也许下次吧。

这个对我来说无论出于什么原因都不起作用,尽管看起来很好。不过我找到了一个替代方法:find . -exec /bin/bash -c 'mv {} `tr [A-Z] [a-z] <<< {}`' ; - John Rix
这需要来自perlprename: dpkg -S "$(readlink -e /usr/bin/rename)"会给出perl: /usr/bin/prename - Tino

6

转换大小写仅适用于字母。因此,这应该很好地工作。

我专注于将a-z范围内的字母从大写转换为小写。任何其他字符都应按原样打印在stdout中...

将路径/到/文件/文件名中的所有文本转换为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

6

针对 Bash3.2.+ | Mac:

read -p 'What is your email? ' email
email=$(echo $email | tr '[:upper:]' '[:lower:]')
email="$email"
echo $email

5
许多答案使用外部程序,这并不真正使用 Bash。如果您知道将有 Bash4 可用,您应该真正使用 ${VAR,,} 表示法(它很容易和酷炫)。对于 Bash 4 之前的版本 (例如我的 Mac 仍然使用 Bash 3.2),我使用 @ghostdog74 的答案来创建一个更便携的版本。其中一个是调用 lowercase 'my STRING' 并获得一个小写版本。我看到了有关将结果设置为变量的评论,但在 Bash 中这并不真正方便,因为我们无法返回字符串。打印是最好的解决方案。可以使用类似 var="$(lowercase $str)" 的方法轻松捕获。 原理 工作原理是通过使用 printf 获取每个字符的 ASCII 整数表示,然后如果是大写转小写,则加上32; 如果是小写转大写,则减去32。然后使用 printf 再将数字转换回字符。从 A 到 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 选项。


我使用类似的技巧来确定Base64中前52个字母的ASCII偏移量:3^4 - 2^4 = 653^4 + 2^4 = 97 - RARE Kpop Manifesto

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接