bash中有没有将字符串转换为小写字符串的方法?

例如,如果我有:

a="Hi all"

我想将其转换为:

"hi all"

当前回答

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

其他回答

在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

要关闭声明属性,请使用+。例如,声明+c字符串。这会影响后续赋值,而不是当前值。

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

编辑:

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

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

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

如果你知道你将有Bash4可用,你真的应该使用${VAR,,}符号(这很简单,很酷)。对于Bash before 4(例如,我的Mac仍然使用Bash 3.2)。我使用@ghostdog74的答案的修正版本创建了一个更便携的版本。

一个可以称为小写的“我的字符串”并获得小写版本。我阅读了关于将结果设置为var的评论,但这在Bash中不是真正可移植的,因为我们不能返回字符串。打印它是最好的解决方案。使用类似于var=“$(小写$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选项中使用此选项。

对于Bash命令行,根据语言环境和国际字母,这可能会起作用(根据其他人的答案组合而成):

$ echo "ABCÆØÅ" | python -c "print(open(0).read().lower())"
abcæøå
$ echo "ABCÆØÅ" | sed 's/./\L&/g'
abcæøå
$ export a="ABCÆØÅ" | echo "${a,,}"
abcæøå

尽管这些变化可能不起作用:

$ echo "ABCÆØÅ" | tr "[:upper:]" "[:lower:]"
abcÆØÅ
$ echo "ABCÆØÅ" | awk '{print tolower($1)}'
abcÆØÅ
$ echo "ABCÆØÅ" | perl -ne 'print lc'
abcÆØÅ
$ echo 'ABCÆØÅ' | dd conv=lcase 2> /dev/null
abcÆØÅ

使用GNU sed:

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

例子:

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

基于Dejay Clayton出色的解决方案,我将大写/小写扩展为转置函数(独立有用),在变量中返回结果(更快/更安全),并添加了BASH v4+优化:

pkg::transpose() { # <retvar> <string> <from> <to>
  local __r=$2 __m __p
  while [[ ${__r} =~ ([$3]) ]]; do
    __m="${BASH_REMATCH[1]}"; __p="${3%${__m}*}"
    __r="${__r//${__m}/${4:${#__p}:1}}"
  done
  printf -v "$1" "%s" "${__r}"
}
pkg::lowercase() { # <retvar> <string>
  if (( BASH_VERSINFO[0] >= 4 )); then
    printf -v "$1" "%s" "${2,,}"
  else
    pkg::transpose "$1" "$2" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
                   "abcdefghijklmnopqrstuvwxyz"
  fi
}
pkg::uppercase() { # <retvar> <string>
  if (( BASH_VERSINFO[0] >= 4 )); then
    printf -v "$1" "%s" "${2^^}"
  else
    pkg::transpose "$1" "$2" "abcdefghijklmnopqrstuvwxyz" \
                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  fi
}

为了简单起见,我没有添加任何set-e支持(或任何错误检查)。。。但除此之外,它通常遵循shellguide和pkg::transpose()试图避免printf-v的任何可能的变量名冲突