今天在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 否 在字串結尾或者在可選的換行符之前爲真。