這部分內(nèi)容可以說是學習shell腳本之前必學的內(nèi)容。如果你這部分內(nèi)容學的越好,那么你的shell腳本編寫能力就會越強。所以不要嫌這部分內(nèi)容啰嗦,也不要怕麻煩,要用心學習。一定要多加練習,練習多了就能熟練掌握了。
在計算機科學中,正則表達式是這樣解釋的:它是指一個用來描述或者匹配一系列符合某個句法規(guī)則的字符串的單個字符串。在很多文本編輯器或其他工具里,正則表達式通常被用來檢索和/或替換那些符合某個模式的文本內(nèi)容。許多程序設(shè)計語言都支持利用正則表達式進行字符串操作。對于系統(tǒng)管理員來講,正則表達式貫穿在我們的日常運維工作中,無論是查找某個文檔,抑或查詢某個日志文件分析其內(nèi)容,都會用到正則表達式。
其實正則表達式,只是一種思想,一種表示方法。只要我們使用的工具支持表示這種思想那么這個工具就可以處理正則表達式的字符串。常用的工具有grep, sed, awk 等,下面筆者就介紹一下這三種工具的使用方法。
【grep / egrep】
筆者在前面的內(nèi)容中多次提到并用到grep命令,可見它的重要性。所以好好學習一下這個重要的命令吧。你要知道的是grep連同下面講的sed, awk都是針對文本的行才操作的。
語法: grep [-cinvABC] ‘word’ filename
-c :打印符合要求的行數(shù)
-i :忽略大小寫
-n :在輸出符合要求的行的同時連同行號一起輸出
-v :打印不符合要求的行
-A :后跟一個數(shù)字(有無空格都可以),例如 –A2則表示打印符合要求的行以及下面兩行
-B :后跟一個數(shù)字,例如 –B2 則表示打印符合要求的行以及上面兩行
-C :后跟一個數(shù)字,例如 –C2 則表示打印符合要求的行以及上下各兩行
http://wiki.jikexueyuan.com/project/linux/images/13_1.png.jpg" alt="13_1.png.jpg" />
以下,筆者舉幾個小例子幫助你好好掌握這個grep工具的用法。
a. 過濾出帶有某個關(guān)鍵詞的行并輸出行號
http://wiki.jikexueyuan.com/project/linux/images/13_7.png.jpg" alt="13_7.png.jpg" />
b. 過濾不帶有某個關(guān)鍵詞的行,并輸出行號
http://wiki.jikexueyuan.com/project/linux/images/13_8.png.jpg" alt="13_8.png.jpg" />
c. 過濾出所有包含數(shù)字的行
http://wiki.jikexueyuan.com/project/linux/images/13_9.png.jpg" alt="13_9.png.jpg" />
在前面也提到過這個”[ ]”的應(yīng)用,如果是數(shù)字的話就用[0-9]這樣的形式,當然有時候也可以用這樣的形式[15]即只含有1或者5,注意,它不會認為是15。如果要過濾出數(shù)字以及大小寫字母則要這樣寫[0-9a-zA-Z]。另外[ ]還有一種形式,就是[^字符] 表示除[ ]內(nèi)的字符之外的字符。
http://wiki.jikexueyuan.com/project/linux/images/13_10.png.jpg" alt="13_10.png.jpg" />
這就表示篩選包含oo字符串,但是不包含r字符。
d. 過濾出文檔中以某個字符開頭或者以某個字符結(jié)尾的行
http://wiki.jikexueyuan.com/project/linux/images/13_11.png.jpg" alt="13_11.png.jpg" />
在正則表達式中,”^”表示行的開始,”$”表示行的結(jié)尾,那么空行則表示”^$”,如果你只想篩選出非空行,則可以使用 “grep -v ‘^$’ filename”得到你想要的結(jié)果。現(xiàn)在想一下,如何打印出不以英文字母開頭的行呢?
http://wiki.jikexueyuan.com/project/linux/images/13_21.png.jpg" alt="13_21.png.jpg" />
e. 過濾任意一個字符與重復字符
http://wiki.jikexueyuan.com/project/linux/images/13_22.png.jpg" alt="13_22.png.jpg" />
“.”表示任意一個字符,上例中,就是把符合r與o之間有兩個任意字符的行過濾出來。
“*”表示零個或多個前面的字符。
http://wiki.jikexueyuan.com/project/linux/images/13_23.png.jpg" alt="13_23.png.jpg" />
‘ooo’ 表示oo, ooo, oooo … 或者更多的’o’。現(xiàn)在你是否想到了’.’ 這個組合表示什么意義?
http://wiki.jikexueyuan.com/project/linux/images/13_24.png.jpg" alt="13_24.png.jpg" />
‘.*’表示零個或多個任意字符,空行也包含在內(nèi)。
f. 指定要過濾字符出現(xiàn)的次數(shù)
http://wiki.jikexueyuan.com/project/linux/images/13_25.png.jpg" alt="13_25.png.jpg" />
這里用到了{ },其內(nèi)部為數(shù)字,表示前面的字符要重復的次數(shù)。上例中表示包含有兩個o 即’oo’的行。注意,{ }左右都需要加上脫意字符’\’。另外,使用{ }我們還可以表示一個范圍的,具體格式是 ‘\’其中n1<n2,表示重復n1到n2次前面的字符,n2還可以為空,則表示大于等于n1次。
上面部分講的grep,另外筆者常常用到egrep這個工具,簡單點講,后者是前者的擴展版本,我們可以用egrep完成grep不能完成的工作,當然了grep能完成的egrep完全可以完成。如果你嫌麻煩,egrep了解一下即可,因為grep的功能已經(jīng)足夠可以勝任你的日常工作了。下面筆者介紹egrep不用于grep的幾個用法。為了試驗方便,筆者把test.txt 編輯成如下內(nèi)容:
rot:x:0:0:/rot:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
operator:x:11:0:operator:/rooot:/sbin/nologin
roooot:x:0:0:/rooooot:/bin/bash
1111111111111111111111111111111
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
a. 篩選一個或一個以上前面的字符
http://wiki.jikexueyuan.com/project/linux/images/13_26.png.jpg" alt="13_26.png.jpg" />
和grep 不同的是,egrep這里是使用’+’的。
b. 篩選零個或一個前面的字符
http://wiki.jikexueyuan.com/project/linux/images/13_27.png.jpg" alt="13_27.png.jpg" />
c. 篩選字符串1或者字符串2
http://wiki.jikexueyuan.com/project/linux/images/13_28.png.jpg" alt="13_28.png.jpg" />
中間有一個’|’表示或者的意思,筆者用這個用的很多,所以這個你最好記一下。
d. egrep中’( )’的應(yīng)用
http://wiki.jikexueyuan.com/project/linux/images/13_29.png.jpg" alt="13_29.png.jpg" />
用’( )’表示一個整體,例如(oo)+就表示1個’oo’或者多個’oo’
http://wiki.jikexueyuan.com/project/linux/images/13_44.png.jpg" alt="13_44.png.jpg" />
【sed 工具的使用】
grep 工具的功能其實還不夠強大,其實說白了,grep實現(xiàn)的只是查找功能,而它卻不能實現(xiàn)把查找的內(nèi)容替換掉。以前用vim的時候,可以查找也可以替換,但是只局限于在文本內(nèi)部來操作,而不能輸出到屏幕上。sed工具以及下面要講的awk工具就能實現(xiàn)把替換的文本輸出到屏幕上的功能了,而且還有其他更豐富的功能。sed和awk都是流式編輯器,是針對文檔的行來操作的。
a. 打印某行 sed -n ‘n’p filename 單引號內(nèi)的n是一個數(shù)字,表示第幾行
http://wiki.jikexueyuan.com/project/linux/images/13_45.png.jpg" alt="13_45.png.jpg" />
b. 打印多行打印整個文檔用 -n ‘1,$’p
http://wiki.jikexueyuan.com/project/linux/images/13_46.png.jpg" alt="13_46.png.jpg" />
c. 打印包含某個字符串的行
http://wiki.jikexueyuan.com/project/linux/images/13_47.png.jpg" alt="13_47.png.jpg" />
上面grep中使用的特殊字符,如’^’, ‘$’, ‘.’, ‘*’等同樣也能在sed中使用。
http://wiki.jikexueyuan.com/project/linux/images/13_48.png.jpg" alt="13_48.png.jpg" />
http://wiki.jikexueyuan.com/project/linux/images/13_49.png.jpg" alt="13_49.png.jpg" />
d. -e 可以實現(xiàn)多個行為
http://wiki.jikexueyuan.com/project/linux/images/13_50.png.jpg" alt="13_50.png.jpg" />
e. 刪除某行或者多行
http://wiki.jikexueyuan.com/project/linux/images/13_51.png.jpg" alt="13_51.png.jpg" />
‘d’ 這個字符就是刪除的動作了,不僅可以刪除指定的單行以及多行,而且還可以刪除匹配某個字符的行,另外還可以刪除從某一行一直到文檔末行。
http://wiki.jikexueyuan.com/project/linux/images/13_52.png.jpg" alt="13_52.png.jpg" />
f. 替換字符或字符串
http://wiki.jikexueyuan.com/project/linux/images/13_53.png.jpg" alt="13_53.png.jpg" />
上例中的’s’就是替換的命令,’g’為本行中全局替換,如果不加’g’,只換該行中出現(xiàn)的第一個。
span>除了可以使用’/’外,還可以使用其他特殊字符例如’#’或者’@’都沒有問題。
http://wiki.jikexueyuan.com/project/linux/images/13_54.png.jpg" alt="13_54.png.jpg" />
現(xiàn)在思考一下,如何刪除文檔中的所有數(shù)字或者字母?
http://wiki.jikexueyuan.com/project/linux/images/13_55.png.jpg" alt="13_55.png.jpg" />
有意思吧,[0-9]表示任意的數(shù)字。這里你也可以寫成[a-zA-Z]甚至[0-9a-zA-Z]
http://wiki.jikexueyuan.com/project/linux/images/13_56.png.jpg" alt="13_56.png.jpg" />
g. 調(diào)換兩個字符串的位置
http://wiki.jikexueyuan.com/project/linux/images/13_57.png.jpg" alt="13_57.png.jpg" />
這個就需要解釋一下了,上例中用’()’把所想要替換的字符括起來成為一個整體,因為括號在sed中屬于特殊符號,所以需要在前面加脫意字符’\’,替換時則寫成’\1’, ‘\2’, ‘\3’ 的形式。除了調(diào)換兩個字符串的位置外,筆者還常常用到在某一行前或者后增加指定內(nèi)容。
http://wiki.jikexueyuan.com/project/linux/images/13_78.png.jpg" alt="13_78.png.jpg" />
h. 直接修改文件的內(nèi)容
sed -i ‘s/:/#/g’ test.txt ,這樣就可以直接更改test.txt文件中的內(nèi)容了。由于這個命令可以直接把文件修改,所以在修改前最好先復制一下文件以免改錯。
sed常用到的也就上面這些了,只要你多加練習就能熟悉它了。為了能讓你更加牢固的掌握sed的應(yīng)用,筆者留幾個練習題給你,希望你能認真完成。
1. 把/etc/passwd 復制到/root/test.txt,用sed打印所有行;
2. 打印test.txt的3到10行;
3. 打印test.txt 中包含’root’的行;
4. 刪除test.txt 的15行以及以后所有行;
5. 刪除test.txt中包含’bash’的行;
6. 替換test.txt 中’root’為’toor’;
7. 替換test.txt中’/sbin/nologin’為’/bin/login’
8. 刪除test.txt中5到10行中所有的數(shù)字;
9. 刪除test.txt 中所有特殊字符(除了數(shù)字以及大小寫字母);
10. 把test.txt中第一個單詞和最后一個單詞調(diào)換位置;
11. 把test.txt中出現(xiàn)的第一個數(shù)字和最后一個單詞替換位置;
12. 把test.txt 中第一個數(shù)字移動到行末尾;
13. 在test.txt 20行到末行最前面加’aaa:’;
現(xiàn)在給出以上練習題的答案,你如果實在想不出如何操作,那你看看答案吧,請盡量多想一下。
1. /bin/cp /etc/passwd /root/test.txt ; sed -n '1,/span>p test.txt
2. sed -n '3,10'p test.txt
3. sed -n '/root/'p test.txt
4. sed '15,/span>d test.txt
5. sed '/bash/'d test.txt
6. sed 's/root/toor/g' test.txt
7. sed 's#sbin/nologin#bin/login#g' test.txt
8. sed '5,10s/[0-9]//g' test.txt
9. sed 's/[^0-9a-zA-Z]//g' test.txt
10. sed 's/(^[a-zA-Z][a-zA-Z])([^a-zA-Z].)([^a-zA-Z])([a-zA-Z][a-zA-Z]*$)/\4\2\3\1/' test.txt
11. sed 's#([^0-9][^0-9])([0-9][0-9])([^0-9].)([^a-zA-Z])([a-zA-Z][a-zA-Z]$)#\1\5\3\4\2#' test.txt
12. sed 's#([^0-9][^0-9])([0-9][0-9])([^0-9].*$)#\1\3\2#' test.txt
13. sed '20,$s/^.*$/aaa:&/' test.txt
【awk工具的使用】
上面也提到了awk和sed一樣是流式編輯器,它也是針對文檔中的行來操作的,一行一行的去執(zhí)行。awk比sed更加強大,它能做到sed能做到的,同樣也能做到sed不能做到的。awk工具其實是很復雜的,有專門的書籍來介紹它的應(yīng)用,但是筆者認為學那么復雜沒有必要,只要能處理日常管理工作中的問題即可。何必讓自己的腦袋裝那么東西來為難自己?畢竟用的也不多,即使現(xiàn)在教會了你很多,你也學會了,如果很久不用肯定就忘記了。鑒于此,筆者僅介紹比較常見的awk應(yīng)用,如果你感興趣的話,再去深入研究吧。
a. 截取文檔中的某個段
http://wiki.jikexueyuan.com/project/linux/images/13_79.png.jpg" alt="13_79.png.jpg" />
解釋一下,-F 選項的作用是指定分隔符,如果不加-F指定,則以空格或者tab為分隔符。
http://wiki.jikexueyuan.com/project/linux/images/13_80.png.jpg" alt="13_80.png.jpg" />
Print為打印的動作,用來打印出某個字段。$1為第一個字段,$2為第二個字段,依次類推,有一個特殊的那就是$0,它表示整行。
http://wiki.jikexueyuan.com/project/linux/images/13_81.png.jpg" alt="13_81.png.jpg" />
注意awk的格式,-F后緊跟單引號,然后里面為分隔符,print的動作要用’{ }’括起來,否則會報錯。print還可以打印自定義的內(nèi)容,但是自定義的內(nèi)容要用雙引號括起來。
http://wiki.jikexueyuan.com/project/linux/images/13_82.png.jpg" alt="13_82.png.jpg" />
b. 匹配字符或字符串
http://wiki.jikexueyuan.com/project/linux/images/13_83.png.jpg" alt="13_83.png.jpg" />
跟sed很類似吧,不過還有比sed更強大的匹配。
http://wiki.jikexueyuan.com/project/linux/images/13_84.png.jpg" alt="13_84.png.jpg" />
可以讓某個段去匹配,這里的’~’就是匹配的意思,繼續(xù)往下看
http://wiki.jikexueyuan.com/project/linux/images/13_85.png.jpg" alt="13_85.png.jpg" />
awk還可以多次匹配,如上例中匹配完root,再匹配test,它還可以只打印所匹配的段。
http://wiki.jikexueyuan.com/project/linux/images/13_86.png.jpg" alt="13_86.png.jpg" />
不過這樣沒有啥意義,筆者只是為了說明awk確實比sed強大。
d. 條件操作符
http://wiki.jikexueyuan.com/project/linux/images/13_87.png.jpg" alt="13_87.png.jpg" />
awk中是可以用邏輯符號判斷的,比如’==’就是等于,也可以理解為“精確匹配”。另外也有’>’, ‘>=’, ‘<’, ‘<=’, ‘!=’ 等等,值得注意的是,即使$3為數(shù)字,awk也不會把它當數(shù)字看待,它會認為是一個字符。所以不要妄圖去拿$3當數(shù)字去和數(shù)字做比較。
http://wiki.jikexueyuan.com/project/linux/images/13_88.png.jpg" alt="13_88.png.jpg" />
這樣是得不到我們想要的效果的。這里只是字符與字符之間的比較,’6’是>’500’的。
http://wiki.jikexueyuan.com/project/linux/images/13_89.png.jpg" alt="13_89.png.jpg" />
上例中用的是’!=’ 即不匹配。
http://wiki.jikexueyuan.com/project/linux/images/13_90.png.jpg" alt="13_90.png.jpg" />
另外還可以使用”&&” 和 “||”表示“并且”和“或者”的意思。
http://wiki.jikexueyuan.com/project/linux/images/13_91.png.jpg" alt="13_91.png.jpg" />
也可以是或者的關(guān)系
http://wiki.jikexueyuan.com/project/linux/images/13_92.png.jpg" alt="13_92.png.jpg" />
d. awk的內(nèi)置變量
常用的變量有:
NF :用分隔符分隔后一共有多少段;
NR :行數(shù)
http://wiki.jikexueyuan.com/project/linux/images/13_93.png.jpg" alt="13_93.png.jpg" />
上例中,打印總共的段數(shù)以及最后一段的值。
http://wiki.jikexueyuan.com/project/linux/images/13_94.png.jpg" alt="13_94.png.jpg" />
可以使用NR作為條件,來打印出指定的行。
http://wiki.jikexueyuan.com/project/linux/images/13_95.png.jpg" alt="13_95.png.jpg" />
e. awk中的數(shù)學運算
http://wiki.jikexueyuan.com/project/linux/images/13_96.png.jpg" alt="13_96.png.jpg" />
awk比較強的地方,還在于能把某個段改成指定的字符串,下面還有更強的呢!
http://wiki.jikexueyuan.com/project/linux/images/13_97.png.jpg" alt="13_97.png.jpg" />
當然還可以計算某個段的總和。
http://wiki.jikexueyuan.com/project/linux/images/13_101.png.jpg" alt="13_101.png.jpg" />
這里的END要注意一下,表示所有的行都已經(jīng)執(zhí)行,這是awk特有的語法,其實awk連同sed都可以寫成一個腳本文件,而且有他們特有的語法,在awk中使用if判斷、for循環(huán)都是可以的,只是筆者認為日常管理工作中沒有必要使用那么復雜的語句而已。
http://wiki.jikexueyuan.com/project/linux/images/13_102.png.jpg" alt="13_102.png.jpg" />
注意這里’( )’的使用。
基本上,正則表達的內(nèi)容就這些了。但是筆者要提醒你一下,筆者介紹的這些僅僅是最基本的東西,并沒有提啊深入的去講sed和awk,但是完全可以滿足日常工作的需要,有時候也許你會碰到比較復雜的需求,如果真遇到了就去請教一下google吧。下面出幾道關(guān)于awk的練習題,希望你要認真完成。
1. 用awk 打印整個test.txt (以下操作都是用awk工具實現(xiàn),針對test.txt);
2. 查找所有包含’bash’的行;
3. 用’:’作為分隔符,查找第三段等于0的行;
4. 用’:’作為分隔符,查找第一段為’root’的行,并把該段的’root’換成’toor’(可以連同sed一起使用);
5. 用’:’作為分隔符,打印最后一段;
6. 打印行數(shù)大于20的所有行;
7. 用’:’作為分隔符,打印所有第三段小于第四段的行;
8. span>用’:’作為分隔符,打印第一段以及最后一段,并且中間用’@’連接(例如,第一行應(yīng)該是這樣的形式 span>“root@/bin/bash”;
9. 用’:’作為分隔符,把整個文檔的第四段相加,求和;
下面給出答案:
1. awk '' test.txt
2. awk '/bash/' test.txt
3. awk -F':' '$3=="0"' test.txt
4. awk -F':' '$1=="root"' test.txt |sed 's/root/toor/'
5. awk -F':' '' test.txt
6. awk -F':' 'NR>20' test.txt
7. awk -F':' '$3<$4' test.txt
8. awk -F':' '' test.txt
9. awk -F':' '{(sum+=$4)}; END ' test.txt