深入淺出linux三劍客之sed必殺技一例 2012-07-31 21:44:40
緣起:上完培訓課後,同學們對sed加正則處理問題的熱情高漲,氛圍相當好,但是,有個別同學還是有些暈乎,因此,撰寫此文。此例如果搞熟練了,sed和正則的大部分常用技術你掌握起來就簡單了。
如果還有不會的,老男孩就拿出最後的講解必殺技(先保密,嘿!),同學們想不會那是不可能的,除非你們自己主動放棄,哈哈。來,開整~!
老男孩linux培訓第三節課程課前第考試一題,問題如下
1.如何取得/etiantian文件的權限對應的數字內容,如-rw-r--r--爲644,要求使用命令取得644或0644這樣的數字。
解答:
這個問題的解答方法我們已經介紹了不下10種了,這裏給大家在深入淺出的講一下最難理解sed特殊用法加正則的實現。
由於難度比較大,爲了讓同學們能瞭解的更清楚明白,我們需要先搭幾個臺階。
臺階1:sed相關命令集選項參數
表1-1 sed命令
命令 |
功能 |
g |
與s聯合使用時,表示對當前行全局匹配替換(與下一個g意義不同) |
p |
打印匹配行 |
s |
常說的查找並替換,用一個字符串替換成另一個 |
表1-2 sed選項
選項 |
功能 |
-e |
允許多項編輯 |
-n |
取消默認輸出 |
詳細的參數,命令信息可以參考《老男孩linux實戰培訓教案-強大的流編輯器sed詳解指南》
臺階2:sed替換基本命令
sed -n 's#▇#▲#g' oldboy.log
[root@oldboy ~]# cat oldboy.log
▇
執行下面命令把▇替換爲▲,注意:原文件沒變。
[root@oldboy ~]# sed 's#▇#▲#g' oldboy.log
▲
提示:其中分隔符#,可以使用/,%,@等替代,詳細使用可參考老男孩的相關文檔。
臺階3:正則表達式基本用法(三劍客grep,sed,awk都支持正則表達式)
特殊字符 |
含義與例子 |
^word |
搜索以字符(word)開頭的行。 |
例:grep -v '^#' oldboy.log 過濾掉以#行首(開始)的行,一般查看配置文件時會用到。 |
|
Word$ |
搜索以字符(word)結尾的行。 |
例:grep -n '!$' oldboy.log 搜索以!(歎號)結尾的行 |
|
.(點號) |
代表切只能代表“任意一個”字符,“一個”是一個任意字符 |
例:grep -n 'e.e' oldboy.log 搜索的字符串可以是(eve),(eae),(eee),(e e),但不能僅有(ee)即e與e之間一定且僅有一個字符,而空格符也是字符 |
|
* |
重複零個或多個的前一個重複字符 |
例:grep -n 'ess*' oldboy.log 找出含有(es),(ess),(esss)等的字符串,注意,因爲(*)可以是0個,所以es 也是符合搜索字符串。另外,因爲(*)爲重複“前一個字符”的符號,因此,在(*)之前必須要緊接一個重複字符。任意字符則爲(.*) |
|
.* |
根據前面的單個字符,我們知道.*匹配所有字符串 |
1)sed的\( \)的功能可以記住正則表達式的一部分,其中,\1爲第一個記住的模式即第一個小括號中的匹配內容,\2第二記住的模式,即第二個小括號中的匹配內容,sed最多可以記住9個。
例1:echo I am oldboy teacher.
如果你想保留一行的單詞,刪除剩下的部分,使用圓括號標記想保留的部分:
本例我們要保留oldboy把其他內容刪除。
[root@oldboy ~]# echo I am oldboy teacher. >test.txt
[root@oldboy ~]# cat test.txt
I am oldboy teacher.
[root@oldboy ~]# sed 's#^.*am \([a-z].*\) tea.*$#\1#g' test.txt
oldboy
命令說明:#命令說明中的□代替空格
a)^.*am□ -->這句的意思是以任意字符開頭到am□爲止,匹配文件中的“I am□”字符串,
b)\([a-z].*\)□-->這句的外殼就是括號\(\),裏面的[a-z]表示匹配26個字母的任何一個,[a-z].* 合起來就是匹配任意多個字符,本題來說就是匹配oldboy字符串,由於oldboy字符串是需要保留的,因此用括號括起來匹配,後面通過\1來取oldboy字符串。
c)□tea.*$ -->表示以空格tea起始任意字符結尾,實際就是匹配oldboy字符串後,緊接着的字符串“□teacher.”.
d)後面被替換的內容中的\1就是取前面的括號裏的內容了,也就是我們要的oldboy字符串。
其實啊,你上了本臺階,本文的主題答案你就該會了。
臺階5:取字符串的技巧 ==>這個臺階的思路技巧是最重要的,也是老男孩linux培訓課程深入淺出的一個小案例。
[root@oldboy ~]# stat /ett|sed -n '4p'
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
1)處理需要的目標(獲取的字符串如本文開篇的644)前的字符串一般用以..開頭(^.*)來匹配開頭,匹配的目標前的結尾寫上實際的字符,如:“^.*(0”表達式匹配“Access: (0”,說到這先給個例子:
[root@oldboy ~]# stat /ett|sed -n '4p'|sed 's#^.*(0##g'
644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
大家看到了吧,644前面的部分已經被刪除了。
2)而處理目標後的內容一般在緊接着目標後匹配的開頭寫上實際的字符,而結尾是用以...結尾(.*$)來匹配,如“/-r.*$”表達式匹配“/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)”在來個例子:
[root@oldboy ~]# stat /ett|sed -n '4p'|sed 's#/-r.*$##g'
Access: (0644
哈哈,這次644後面的又被刪除了。
有一點要注意,就是實際字符的選取最好要唯一,因爲正則表達式是貪婪的,它總是儘可能的匹配更遠的符合匹配的內容。另外不要落了字符串中的空格。
合起來就是答案了。
[root@oldboy ~]# stat /ett|sed -n '4p'|sed 's#^.*(0##g'|sed 's#/-r.*$##g'
644
先刪除目標前面的,在刪除目標後的,這是應用了兩次sed,每次只匹配了一半,第一次匹配目標前,第二次匹配目標後,那麼能不能直接匹配整行字符串呢?
所有的臺階都上來後,接下來就引出本文的主題。
關鍵人物出場:
有了前面的臺階,現在這道題就簡單多了,好先拋出答案,然後在分析說明。
[root@oldboy ~]# stat /ett|sed -n 's#^.*(0\([0-7].*\)\/-.*$#\1#gp'
644
命令說明:
a)“^.*(0”匹配目標前的內容,這個不用再解釋了吧,這裏匹配的是“Access: (0
b)“\/-.*$” 匹配目標後的內容,這個也不用再解釋了吧,這裏匹配的是“/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)”
c)這中間的644就是我們要打印的結果,所以要用括號括起來呦,於是\(\)就不用再解釋了,[0-7].*就是匹配644,匹配到斜線/前爲止,雖然不是精確匹配但是這裏夠用了。因此,“\([0-7].*\)”匹配的就是“644”了,前面的臺階已經講過了,匹配模式中\(\)的內容,可以用\1來取出(如果是第二個擴號就是\2,類推),所以啊,644的結果就出來拉。
後記小結:
當然了,匹配方法不只這一種,還可以寫出很多種,但是老男孩老師給出的是偶自己總結的一個易於初學者記憶的思路,如果大家有更好的記憶的方法,可以告訴我啊。
同學們可以拿取網卡的IP地址練手,檢驗下,看看你們的刀磨快了麼?哈哈!
附帶其他解決答案彙總:
[root@oldboy ~]# touch /ett
1)
[root@oldboy ~]# stat /ett|sed -n '4p'|awk -F '[(/]' '{print $2}'
0644
2)
[root@oldboy ~]# stat /ett|sed -n '4p'|cut -d "/" -f1|cut -d "(" -f2
0644
3)
[root@oldboy ~]# stat /ett|head -4|tail -1|cut -d "/" -f1|cut -d "(" -f2
0644
4)
[root@oldboy ~]# stat /ett|head -4|tail -1|awk -F "/" '{print $1}'|awk -F "(" '{print $2}'
0644
5)
[root@oldboy ~]# stat -c %a /ett
644
6)
[root@oldboy ~]# stat /ett|sed -n '4p'|sed 's#^.*(0##g'|sed 's#/-.*$##g'
644
7)
[root@oldboy ~]# stat /ett|sed -n 's#^.*(0\([0-7].*\)\/-.*$#\1#gp'
644
8)
[root@oldboy ~]# stat /ett|sed -n '4p'|sed -ne 's/^.* (0\([^ ]*\)\/-.*$/\1/gp'
644
9)
[root@oldboy ~]# stat /ett|sed -n '/Access: (/p'|sed -r 's/^.*\(0(.*)\/-.*$/\1/'
644
[root@oldboy ~]# stat /ett|sed -n '4p'|sed -e 's/^Access: (\([0-9]*\)\/-rw-r.*$/\1/g'
0644
10)
[root@oldboy ~]# stat /ett|grep "Access: (0"|cut -c 10-13
0644
老男孩語錄⑧⑥-態度決定一切!
好的態度就是成功的一半!