10

我是awk新手,所以请多多包涵。

目标是更改字符串的大小写,使每个单词的第一个字母为大写,其余字母为小写。(为了简单起见,这里的“单词”被定义为严格的字母字符;所有其他字符都被视为分隔符。)

我从本网站的另一篇文章中学到了一种很好的方法,可以使用以下 awk 命令将每个单词的首字母大写:

echo 'abce efgh ijkl mnop' | awk '{for (i=1;i <= NF;i++) {sub(".",substr(toupper($i),1,1),$i)} print}' --> Abcd Efgh Ijkl Mnop

通过在 awk 命令前加上 tr 命令可以轻松完成剩余的字母小写:

echo 'aBcD EfGh ijkl MNOP' | tr [A-Z] [a-z] | awk '{for (i=1;i <= NF;i++) {sub(".",substr(toupper($i),1,1),$i)} print}' --> Abcd Efgh Ijkl Mnop

但是,为了了解更多关于 awk 的信息,我想用类似的 awk 结构将除第一个字母以外的所有字母的大小写更改为小写。我使用正则表达式\B[A-Za-z]+匹配单词的所有字母,但第一个字母除外,并使用 awk 命令substr(tolower($i),2)以小写形式提供相同的字母,如下所示:

echo 'ABCD EFGH IJKL MNOP' | awk '{for (i=1;i <= NF;i++) {sub("\B[A-Za-z]+",substr(tolower($i),2),$i)} print}' --> Abcd EFGH IJKL MNOP

请注意,第一个单词已正确转换,但其余单词保持不变。我将非常感谢解释为什么剩余的单词没有正确转换以及如何让他们这样做。

4

4 回答 4

8

问题是\B(零宽度非单词边界)似乎仅在行首匹配,因此$1有效,但$2后续字段与正则表达式不匹配,因此它们不会被替换并保持大写。不知道为什么\B除了第一个字段之外不匹配... B 应该匹配任何单词中的任何位置:

echo 'ABCD EFGH IJKL MNOP' | awk '{for (i=1; i<=NF; ++i) { print match($i, /\B/); }}'
2   # \B matches ABCD at 2nd character as expected
0   # no match for EFGH
0   # no match for IJKL
0   # no match for MNOP

无论如何,要实现您的结果(仅大写该行的第一个字符),您可以对$0(整行)进行操作,而不是使用for循环:

echo 'ABCD EFGH IJKL MNOP' | awk '{print toupper(substr($0,1,1)) tolower(substr($0,2)) }'

或者,如果您仍想单独大写每个单词,但awk仅使用:

awk '{for (i=1; i<=NF; ++i) { $i=toupper(substr($i,1,1)) tolower(substr($i,2)); } print }'
于 2013-01-03T13:43:31.203 回答
4

使用sub()函数或其他(如gsub()等)匹配正则表达式时,最好以以下形式使用:

sub(/regex/, replacement, target)

这与您所拥有的不同:

sub("regex", replacement, target)

所以你的命令变成:

awk '{ for (i=1;i<=NF;i++) sub(/\B\w+/, substr(tolower($i),2), $i) }1'

结果:

Abcd Efgh Ijkl Mnop

这篇关于字符串函数的文章可能值得一读。HTH。


我应该说有更简单的方法来完成你想要的,例如使用GNU sed

sed -r 's/\B\w+/\L&/g'
于 2013-01-03T13:34:09.373 回答
3

sub我的解决方案是首先使用substr您的正则表达式来获取第一部分:

echo 'ABCD EFGH IJKL MNOP' | awk '{for (i=1 ; i <= NF ; i++) {sub(substr($i,2),tolower(substr($i,2)),$i)} print }'
Abcd Efgh Ijkl Mnop
于 2013-01-03T14:01:00.773 回答
1

您必须在 \B 之前添加另一个 \ 字符

 echo 'ABCD EFGH IJKL MNOP' | awk '{for (i=1;i <= NF;i++)
 {sub("\\B[A-Za-z]+",substr(tolower($i),2),$i)} print}'

只需 \B awk 就给了我这个警告:

awk:命令。行:1:警告:转义序列\B' treated as plainB'

于 2013-01-03T13:29:22.773 回答