bash中有没有将字符串转换为小写字符串的方法?
例如,如果我有:
a="Hi all"
我想将其转换为:
"hi all"
bash中有没有将字符串转换为小写字符串的方法?
例如,如果我有:
a="Hi all"
我想将其转换为:
"hi all"
当前回答
尽管这个问题有多古老,而且与技术龙的答案相似。我很难找到一个能够跨大多数平台(我使用的平台)以及旧版本bash移植的解决方案。我还对数组、函数以及使用prints、echo和临时文件来检索琐碎变量感到失望。到目前为止,这对我来说非常有效,我想我会分享。我的主要测试环境是:
GNUbash,版本4.1.2(1)-发布(x86_64-redhat-linux-GNU)GNUbash,版本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样式,用于遍历字符串。如果您以前没有看到过类似的内容,请选择下面的行这是我学到的。在这种情况下,该行检查输入中是否存在字符${input:$i:1}(小写),如果存在,则用给定的字符${ucs:$j:1}(大写)替换它,并将其存储回输入中。
input="${input/${input:$i:1}/${ucs:$j:1}}"
其他回答
有多种方式:
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”)搞糊涂了!:-)
如果使用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函数中,使用${!1//\'/“'\'”}而不是${!1},即使字符串有引号,也可以使用此函数。
因此,我尝试对每个实用程序使用共识方法执行一些更新的基准测试,但我没有多次重复一个小集合,而是。。。
以UTF-8编码的多字节Unicode字符填充到边缘的1.85 GB.txt文件中,为了均衡I/O方面,同时还强制所有人使用LC_ALL=C,以确保公平竞争
————————————————————————————————————————
准确地说,bsd-sed和gnu-sed都相当平庸。我甚至不知道bsd sed在做什么,因为他们的xxhash不匹配python3是否试图使用Unicode字母大小写?(即使我已经强制设置了区域设置LC_ALL=C)tr是最极端的到目前为止,gnutr是最快的bsd tr非常残暴perl5比我拥有的任何awk变体都快,除非你可以使用mawk2一次加载整个文件,以便稍微超过perl5:2.935秒mawk2对每15秒3.081秒在awk中,gnu-gawk的速度最慢,中间是mawk 1.3.4,最快是mawk1.9.9.6:比gawk节省50%以上的时间.(我没有把时间浪费在无用的macosx nawk上)
.
out9: 1.85GiB 0:00:03 [ 568MiB/s] [ 568MiB/s] [ <=> ]
in0: 1.85GiB 0:00:03 [ 568MiB/s] [ 568MiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C mawk2 '{ print tolower($_) }' FS='^$'; )
mawk 1.9.9.6 (mawk2-beta)
3.07s user 0.66s system 111% cpu 3.348 total
85759a34df874966d096c6529dbfb9d5 stdin
out9: 1.85GiB 0:00:06 [ 297MiB/s] [ 297MiB/s] [ <=> ]
in0: 1.85GiB 0:00:06 [ 297MiB/s] [ 297MiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C mawk '{ print tolower($_) }' FS='^$'; )
mawk 1.3.4
6.01s user 0.83s system 107% cpu 6.368 total
85759a34df874966d096c6529dbfb9d5 stdin
out9: 23.8MiB 0:00:00 [ 238MiB/s] [ 238MiB/s] [ <=> ]
in0: 1.85GiB 0:00:07 [ 244MiB/s] [ 244MiB/s] [============>] 100%
out9: 1.85GiB 0:00:07 [ 244MiB/s] [ 244MiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C gawk -be '{ print tolower($_) }' FS='^$';
GNU Awk 5.1.1, API: 3.1 (GNU MPFR 4.1.0, GNU MP 6.2.1)
7.49s user 0.78s system 106% cpu 7.763 total
85759a34df874966d096c6529dbfb9d5 stdin
out9: 1.85GiB 0:00:03 [ 616MiB/s] [ 616MiB/s] [ <=> ]
in0: 1.85GiB 0:00:03 [ 617MiB/s] [ 617MiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C perl -ne 'print lc'; )
perl5 (revision 5 version 34 subversion 0)
2.70s user 0.85s system 115% cpu 3.081 total
85759a34df874966d096c6529dbfb9d5 stdin
out9: 1.85GiB 0:00:32 [57.4MiB/s] [57.4MiB/s] [ <=> ]
in0: 1.85GiB 0:00:32 [57.4MiB/s] [57.4MiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C gsed 's/.*/\L&/'; ) # GNU-sed
gsed (GNU sed) 4.8
32.57s user 0.97s system 101% cpu 32.982 total
85759a34df874966d096c6529dbfb9d5 stdin
out9: 1.86GiB 0:00:38 [49.7MiB/s] [49.7MiB/s] [ <=> ]
in0: 1.85GiB 0:00:38 [49.4MiB/s] [49.4MiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C sed 's/.*/\L&/'; ) # BSD-sed
37.94s user 0.86s system 101% cpu 38.318 total
d5e2d8487df1136db7c2334a238755c0 stdin
in0: 313MiB 0:00:00 [3.06GiB/s] [3.06GiB/s] [=====>] 16% ETA 0:00:00
out9: 1.85GiB 0:00:11 [ 166MiB/s] [ 166MiB/s] [ <=>]
in0: 1.85GiB 0:00:00 [3.31GiB/s] [3.31GiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C python3 -c "print(open(0).read().lower()))
Python 3.9.12
9.04s user 2.18s system 98% cpu 11.403 total
7ddc0b5cbcfbbfac3c2b6da6731bd262 stdin
out9: 2.51MiB 0:00:00 [25.1MiB/s] [25.1MiB/s] [ <=> ]
in0: 1.85GiB 0:00:11 [ 171MiB/s] [ 171MiB/s] [============>] 100%
out9: 1.85GiB 0:00:11 [ 171MiB/s] [ 171MiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C ruby -pe '$_.downcase!'; )
ruby 2.6.8p205 (2021-07-07 revision 67951) [universal.arm64e-darwin21]
10.46s user 1.23s system 105% cpu 11.073 total
85759a34df874966d096c6529dbfb9d5 stdin
in0: 1.85GiB 0:00:01 [1.01GiB/s] [1.01GiB/s] [============>] 100%
out9: 1.85GiB 0:00:01 [1.01GiB/s] [1.01GiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C gtr '[A-Z]' '[a-z]'; ) # GNU-tr
gtr (GNU coreutils) 9.1
1.11s user 1.21s system 124% cpu 1.855 total
85759a34df874966d096c6529dbfb9d5 stdin
out9: 1.85GiB 0:01:19 [23.7MiB/s] [23.7MiB/s] [ <=> ]
in0: 1.85GiB 0:01:19 [23.7MiB/s] [23.7MiB/s] [============>] 100%
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C tr '[A-Z]' '[a-z]'; ) # BSD-tr
78.94s user 1.50s system 100% cpu 1:19.67 total
85759a34df874966d096c6529dbfb9d5 stdin
( time ( pvE0 < "${m3t}" | LC_ALL=C gdd conv=lcase ) | pvE9 ) | xxh128sum | lgp3; sleep 3;
out9: 0.00 B 0:00:01 [0.00 B/s] [0.00 B/s] [<=> ]
in0: 1.85GiB 0:00:06 [ 295MiB/s] [ 295MiB/s] [============>] 100%
out9: 1.81GiB 0:00:06 [ 392MiB/s] [ 294MiB/s] [ <=> ]
3874110+1 records in
3874110+1 records out
out9: 1.85GiB 0:00:06 [ 295MiB/s] [ 295MiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C gdd conv=lcase; ) # GNU-dd
gdd (coreutils) 9.1
1.93s user 4.35s system 97% cpu 6.413 total
85759a34df874966d096c6529dbfb9d5 stdin
% ( time ( pvE0 < "${m3t}" | LC_ALL=C dd conv=lcase ) | pvE9 ) | xxh128sum | lgp3; sleep 3;
out9: 36.9MiB 0:00:00 [ 368MiB/s] [ 368MiB/s] [ <=> ]
in0: 1.85GiB 0:00:04 [ 393MiB/s] [ 393MiB/s] [============>] 100%
out9: 1.85GiB 0:00:04 [ 393MiB/s] [ 393MiB/s] [ <=> ]
3874110+1 records in
3874110+1 records out
out9: 1.85GiB 0:00:04 [ 393MiB/s] [ 393MiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C dd conv=lcase; ) # BSD-dd
1.92s user 4.24s system 127% cpu 4.817 total
85759a34df874966d096c6529dbfb9d5 stdin
————————————————————————————————————————
通过一次加载所有文件,并在单个函数调用中对所有1.85 GB执行tolower(),可以人为地使mawk2比perl5更快::
( time ( pvE0 < "${m3t}" |
LC_ALL=C mawk2 '
BEGIN { FS = RS = "^$" }
END { print tolower($(ORS = "")) }'
) | pvE9 ) | xxh128sum| lgp3
in0: 1.85GiB 0:00:00 [3.35GiB/s] [3.35GiB/s] [============>] 100%
out9: 1.85GiB 0:00:02 [ 647MiB/s] [ 647MiB/s] [ <=> ]
( pvE 0.1 in0 < "${m3t}" | LC_ALL=C mawk2 ; )
1.39s user 1.31s system 91% cpu 2.935 total
85759a34df874966d096c6529dbfb9d5 stdin
tr:
a="$(tr [A-Z] [a-z] <<< "$a")"
AWK:
{ print tolower($0) }
sed:
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
这是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可以添加其他字符