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

例如,如果我有:

a="Hi all"

我想将其转换为:

"hi all"

当前回答

使用GNU sed:

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

例子:

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

其他回答

对于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ÆØÅ

对于仅使用内置的标准外壳(没有bashms):

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

这是JaredTS486方法的一个更快的变体,该方法使用本地Bash功能(包括Bash版本<4.0)来优化他的方法。

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

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

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

0.46s表示我的小写方式;大写0.96s奥韦洛菲的小写方法为1.16秒;大写字母为1.59tr转小写为3.67s;大写3.81s11.12s用于ghostdog74的小写方法;大写31.41秒26.25秒用于技术龙的小写方式;大写字母为26.21sJaredTS486的小写方法为25.06s;大写27.04s

445个字符的1000次迭代的计时结果(包括威特·拜纳的诗歌《罗宾》):

我的小写方法是2s;大写字母为12s4s表示tr为小写;大写字母为4s奥韦洛菲(Orwellophile)的小写方法为20秒;大写字母为29sghostdog74的小写方式为75秒;669表示大写。值得注意的是,在比赛占优势的测试和失误占优势的考试之间,表现差异有多大467秒表示技术龙的小写方式;大写字母为449660秒用于JaredTS486的小写方法;大写为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}"
}

方法很简单:当输入字符串中有剩余的大写字母时,找到下一个,并用其小写变体替换该字母的所有实例。重复此操作,直到替换所有大写字母。

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

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

将转换后的字符串存储到变量中。跟踪对我有用-$SOURCE_NAME至$TARGET_NAME

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

有多种方式:

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

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在这一个。即使使用shopt-u nocasematch;,也不适用于我(GNUbash版本4.2.46和4.0.33(行为2.05b.0相同,但没有实现nocasemach));。取消设置nocasematch会导致[[“fooBaR”==“fooBaR”]]与OK匹配,但奇怪的是,[b-z]与[A-z]不匹配。Bash被双重否定(“未设置nocasematch”)搞糊涂了!:-)