今天在CU看到一个帖子,其中有这个问题很值得启发思考与学习。
如何使用shell将文件中单词首字母转换为大写?
这个问题看似简单,实现起来确不易,即使是在vi甚至是wordpad条件下也很困难。还好我们有伟大的sed和awk。
先来分析问题,将文件中的每个单词的首字母转换为大写,这里有两个难点。
1. 如何区分单词的边界?
2. 大小写转换如何实现?
对於单词边界的区分,一般英文习惯下我们输入单词前面都是空格或换行符号;大小写转换在awk中有函数toupper()。先尝试使用awk解决。
- [root@managevm1 ~]# cat test
- consult section 3.1 in the Owner
- and operator guide for a description of the tape drives
- available on your system.
- [root@managevm1 ~]# awk '{for (i=1;i<=NF;i++)printf toupper(substr($i,0,1))substr($i,2,length($i))" ";printf "\n"}' test
- Consult Section 3.1 In The Owner
- And Operator Guide For A Description Of The Tape Drives
- Available On Your System.
解析:在awk中用for循环遍历每行中的的每个字段,NF为每行最后一个字段,如$1,$2,$3,$NF为第一,二,三,最后一个字段。toupper函数将字符串转换为大写。substr(string,M,[N])用于对string取从第M到N的子串。length(string)返回字符串长度。
再来看看使用sed如何实现。
我们可能会这样写:
- s/\([ ]\)a/\1A/g
- s/\([ ]\)b/\1B/g
- s/\([ ]\)c/\1C/g
- .......
- s/\([ ]\)z/\1Z/g
但这样无法匹配每行开头的单词,因为每行开头是没有空格的。于是我们加上如下
- s/^a/A/
- s/^b/B/
- .....
- s/^z/Z/
OH,要不要这么复杂!来看看这一种写法吧!另你恍然大悟吧?
- [root@managevm1 ~]# sed 's/\b[a-z]/\U&/g' test
- Consult Section 3.1 In The Owner
- And Operator Guide For A Description Of The Tape Drives
- Available On Your System.
解析,\b \U是正则表达式的元符号。\b用来定界,\U大小转换。&是sed前面替换的字符串。
表 5-7。字母数字正则元符号
符号 原子性 含义
\0 是 匹配空字符(ASCII NUL)。
\NNN 是 匹配给出八进制的字符,最大值为\377。
\n 是 匹配前面第n个捕获字串(十进制)。
\a 是 匹配警钟字符(BEL)。
\A 否 如果在字串的开头为真
\b 是 匹配退各字符(BS)。
\b 否 在字边界为真
\B 否 不在字边界时为真
\cX 是 匹配控制字符 Control-x(\cZ,\c[,等)。
\C 是 匹配一个字节(C字符),甚至在utf8中也如此(危险)
\d 是 匹配任何数字字符
\D 是 匹配任何非数字字符
\e 是 匹配逃逸字符(ASCII ESC,不是反斜杠)。
\E —— 结束大小写(\L,\U)或者掩码(\Q)转换
\f 是 匹配进页字符(FF)。
\G 否 如果在前一个m//g的匹配结尾位置时为真
\l —— 只把下一个字符变成小写
\L —— 把\E以前的字母都变成小写
\n 是 匹配换行符字符(通常是NL,但是在Mac上是CR)。
\N{NAME} 是 匹配命名字符(\N{greek:Sigma})。
\p{PROP} 是 匹配任何有命名属性的字符
\P{PROP} 是 匹配任何没有命名属性的字符
\Q —— 引起(消元)直到\E前面的字符
\r 是 匹配返回字符(通常是CR,但是在Mac上是NL)。
\s 是 匹配任何空白字符。
\S 是 匹配任何非空白字符。
\t 是 匹配水平制表符(HT)。
\u —— 只把下一个字符变成标题首字符
\U —— 大写(不是标题首字符)\E 以前的字符。
\w 是 匹配任何“字”字符(字母数字加"_" )。
\W 是 匹配任何“非字”字符。
\x{abcd} 是 匹配在十六进制中给出的字符。
\X 是 匹配Unicode里的”组合字符序列“字串。
\z 否 只有在字串结尾时为真
\Z 否 在字串结尾或者在可选的换行符之前为真。