1. 程式人生 > >GNU/Linux 正則表示式與三劍俠(grep,sed,awk)(精)

GNU/Linux 正則表示式與三劍俠(grep,sed,awk)(精)

相關好文章推薦:

GNU 的正則表示式

傳聞中三劍俠的威名響徹雲霄,傳說中若沒有正則表示式的神功,三劍俠也是芸芸眾生,江湖中傳言"欲成劍俠,先練神功",不管傳說或傳聞我都信。

度度果然不是蓋的,一下就拔出了正則的歷史,不看不知道,一看就大有來頭,大約就是國外幾位猛人科學家在搞一個偉大的工程時誕生了正則數學表示式,隨後被目光深遠的Ken,將這正則表示式引入於Unix的編輯器中。後面的事情大家都很清楚了,三劍俠大鬧搜尋界,傳說由此開始,而故事遠遠沒有落幕,......

從正則的歷史中,可以看出正則表示式對英文是100%的原生,也就是為英文的而生(會不會因英文而滅,不好說,或許明天你就嫁給我了,會有中文的筆畫正則也說不定,拼音正則也行),主要應用於按某規則搜尋匹對英文中的內容

,所以對英文的26個字母不熟悉的同志要努力下了(俺初中英語非常6,所以這26個英文字母,蒙著眼睛也知道誰是誰,誰跟誰,一點不模糊。),那麼對英文的結構,就需要了解下,英文單詞由英文字母排列組合並以空格來分隔每一個單詞,這個就是我們下工夫及體會的本質(英文字母,單詞[ 每個字母所在的位置決定了單詞意義 ],空格構成英語文字的三劍豪)。

對所愛,要重複大聲說:trust me , I love you

1 字元(我思故我在,能想的,都是字元)

2 字母的不同排序組合構成不同的單詞

3 空格分隔單詞(只要有某字元分隔單詞,採用什麼字元都可以)

圖解正則表示式的對英語文字的看法

示例:I love u 的字元與位置關係
字元 I 空格 l o v e 空格 u
位置 0 1 2 3 4 5 6 7 8

對英文(其他字符集)搜尋的兩種模式(字元[ 組合 ]位置,字元[ 組合 ]的排序)

字元 [ 組合 ] 位置搜尋方式

位置搜尋方式比較單一,目的是確定字元(單詞)在整行字元排列位置上,常用^來表示字元(單詞)在一行文字的最開始位置(一行的頂格位置), $ 來表示字元(單詞)在一行文字的最末尾的位置(一行的最末尾位置),任意單字元*表示所有的位置,\b作用就是單詞與單詞之間的位置,\B作用就是某單詞內字母與字母之間的位置,這兩個大小傻B不能用說,要直接上才明白。

這裡有個知識點,就是shell的萬用字元及單引號、雙引號、無引號與正則表達的元字元的應用

shell 命令列輸入命令且回車 --> shell會對輸入命令及引數進行解析 (為了讓你的意圖能正確到達正則,瞭解shell的解析是必要的)--> 正則表示式解析工作

 ^、$、任意字元*

 * 表示重複前一個字元(單詞)0~無限次,當等於0的情況,z*就表示位置了(其中z可以是任意的字元)。

通過sed的替換,將一句話的所有位置都替換6,替換後的位置比之前的位置又擴容了

[[email protected] ~]# echo "It's my life.It's my life." | grep --color 'It'   
It's my life.It's my life.
[[email protected] ~]# echo "It's my life.It's my life." | grep --color '^It'
It's my life.It's my life.
[[email protected] ~]# 
[[email protected] ~]# echo "It's my life.It's my life." | sed 's/z*/6/g'
6I6t6'6s6 6m6y6 6l6i6f6e6.6I6t6'6s6 6m6y6 6l6i6f6e6.6
[[email protected] ~]# echo "It's my life.It's my life." | sed 's/^a*/6/g'
6It's my life.It's my life.
[[email protected] ~]# 
[[email protected] ~]# echo "It's my life.It's my life." | grep --color 'fe\.$'  
It's my life.It's my life.
[[email protected] ~]# echo "It's my life.It's my life." | sed -r 's/(yf)*$/6/g'
It's my life.It's my life.6
  \B、\b
 \B:一單詞內的字母,不含首或尾字母(組合)  \b:單詞與單詞之間的分隔,以單詞的首或尾字母(組合)及非\w字元明確分隔。注意要明確是你認為的單詞
[[email protected] ~]# echo 'live love life ifthen thenif' |grep --color '\Bi\B'
live love life ifthen thenif
[[email protected] ~]#
[[email protected] ~]# echo 'live love life left list' |grep --color '\bl\w\+e\b'           
live love life left list
[[email protected] ~]#

 常用的組合:

^$ 表示空行。第一個位置同時是開始也是結束。

 
[[email protected] ~]# echo 'live love life left list' |grep --color '\bl.*\+e\b'
live love life left list
[[email protected] ~]# 

 注意:

\bl.*\+e\b   的寫法是認為"live love life"是一個整體,可以理解為一個單詞,因為.*的意思是表示所有的字元

\bl\w\+\e\b 的寫法是認為live love life 是三個詞。

[[email protected] ~]#  echo 'live,love-life;left:list' |grep --color '\bl\w\+e\b'    
live,love-life;left:list
[[email protected] ~]# 

字元 [ 組合 ] 的排序搜尋方式

這個相對比中文的筆畫組合要好很多了,欲知詳情(正則的元字元用法)請點選文章首部的相關檔案推薦,我只做一下分類總結(沒法子,功力不足,小弟我目前只站在他們的腳尖上。)。

由於英文單詞由字母組成,所以字母對單詞的特性就顯現出現了,如在一個單詞中,某字母或某組字母出現的次數、自定義範圍內的字元、各種字元的分類。無論如何搗鼓,都是人類書寫的習慣。放大招如下表:

 擴充套件正則(ERE)相容基礎正則(BRE)
shell萬用字元 正則表示式元字元
說明 數量 符號 數量 說明 特點 所屬
 任意字元及數量  0 ~ 無限個  * 0~ 無限  對前一個字元或一組字元定義至少重複出現的次數範圍0~無限次  數量類 基礎正則
. 1 一個任意的字元 數量類 基礎正則
+ 1 ~ 無限 對前一個字元或一組字元定義至少重複出現的次數範圍1~無限次 數量類 擴充套件正則
一個任意的字元 1 0 ~ 1 對前一個字元或一組字元定義至少出現0或1次 數量類 擴充套件正則
{n,m} n ~ m 對前一個字元或一組字元定義至少重複出現的次數範圍n~m次 數量類 擴充套件正則
{n,} n~ 無限 對前一個字元或一組字元定義至少重複出現的次數範圍n~無限次 數量類 擴充套件正則
{,m} 0~m次 對前一個字元或一組字元定義至少重複出現的次數範圍0-m次 數量類 擴充套件正則
{m} m 對前一個字元或一組字元定義重複出現m次 資料類 擴充套件正則

touch {1..2}.txt

touch {a..c}.txt

序列1 ~ 序列2 { 序列1..序列2 }
[ 字元1 ... 字元n ] 1

中括號內的任意一個字元

[a-z] 表示小寫字母的任意一個

[A-Z] 表示大寫字母的任意一個

[0-9] 表示數字的任意一個

[^0-9] 表示任意一個字元但不能是數字

[i s,_-] 表示只能是字母i,s,空格,逗號,減號中的任意一個

範圍類

(集合類)

 基礎正則
[:alpha:] 1

等價於[a-zA-Z],即是26個大小寫英文字母

呼叫方式 [[:alpha:]]

字元分類 基礎正則
其餘的字元分類

其他[:系列:]的,到推薦的文章或度一下就有一噸糧食了。

字元分類 基礎正則 
\W 1 非\w的所有可見或不可見的任意字元 

字元分類

基礎正則 
\w 1  大小寫英文字母,數字,下劃線

字元分類 

基礎正則 
\S 1  任意一個可見的字元(理解用筆在紙上能寫出的符號都是可見)

字元分類 

基礎正則
\s 1  非可寫的字元(理解無需用筆在紙上寫出的字元,如空格)

字元分類 

基礎正則
^ 位置類 基礎正則
獲取變數值 $ 位置類 基礎正則
\b \< 單詞首 ;\> 單詞尾,使用方式與 \b 基本一致 位置類 基礎正則
\B 位置類 基礎正則
字元或組 | 字元或組 1 左邊或右邊的字元或一組字元只能出現其一 2選1 擴充套件正則
1或多個字元 1

功能一:將括號裡面的字元組成一組

功能二:可以給某些搜尋命令(sed,grep)做反向引用

分組 擴充套件正則

 \W,\S,\w,\s,及其他的字元分類都很好用。下圖是關於大S配小w,小S配大W的人脈關係圖

 正則表示式工作寫真

正則表示式越模糊匹配,效能越低;反之效能越高(多思考等效的正則表示式是深入理解正則及調優的關鍵
 正則的工作原理概念模型 可以在度度找一下RegexBuddy 這個正則軟體,能反映出正則表示式匹對內容大約需要多少次,正則表示式的效能調優參考

舉例:http://www.oldboyedu.com:80/html/html-tutorial.html

提取:http,www.oldboyedu.com,80,/html/html-tutorial.html,四段內容

分析:該url是一個複合詞(字母數字標點符號混合),從要求中,發現有兩個詞是同類詞,

一個是http,一個是80,另兩個是複合詞,一個是字母與點號,一個是字母斜線減號點號組成。其中,:或//要去掉

基本思路,使用二選一的方式,將二選一變成多選

基礎解題關鍵:如何將內容分解對應的詞或複合詞

1:同類詞http、80,是同屬一類字元  -->  \w+ 或 [a-z0-9]+ 就可以輕鬆啪它下來了

2:複合詞www.oldboy.com.cn 域名類 --> 特點就是.xxx是重複的 --> \w+(\.\w+)+

3:複合詞/html/html-tutorial.html 路徑類 --> 固定/html/後面可以無限任意字元  -->/\w+/.*

答案:

[[email protected] ~]# a=http://www.oldboyedu.com:80/html/html-tutorial.html

[[email protected] ~]# echo $a | grep -oE '[a-z]+|\w+(\.\w+){2}|[0-9]{2}|/\w+/.*'  #四個表示式的順序與url的四個內容一致,解法一:一一對應

[[email protected] ~]# echo $a | grep -Eo '\w+|\w+(\.\w+)+|/\w+/.*'  #\w+ 匹配出 http 及 80,解法二:提取同類的字元為一類

[[email protected] ~]# echo $a | grep -oE '[a-z.0-9]+|[^0-9]+$'  #[^0-9]+$ 匹配出 /html/html-tutorial.html ,解法三:與解法二類似,引用位置關係

機制解題的關鍵:在理解基礎解題的基礎上,搭配*可變0及正則匹配流程

[[email protected] ~]# echo $a | grep -oE '(/\w+/.*)*([^:/])*'

解題過程:

1 (/\w+/.*)*  由於有*,整個表示式可以變0,所以從url的第一個字元開始匹對都失敗,直到/html/html-tutorial.html 才匹對成功,產生佔有字元。

2 匹對過程: (/\w+/.*)* 已經匹對完整個內容了,但正則表示式 ([^:/])* 還沒有匹對,所以 ([^:/])*從頭開始匹對

3 貪婪與回溯:當 ([^:/])* 將/html/html-tutorial.html分解多個單詞,而 (/\w+/.*)* 就一個單詞,按貪婪原則 ,(/\w+/.*)*勝出。

注意:一個正則表示式產生佔有字元,不代表其他表示式就不會對該佔有字元對應的內容重新匹對,因為佔有字元的最終確定是貪婪機制

佔有字元零寬度是正則匹對原理的基礎

佔有字元主要是對字元 [ 組合 ] 的排序搜尋方式的工作機制:核心就是正則表示式不會重複匹對已經匹配出結果的內容,及得出臨時的結果(佔有字元)。

使用 RegexBuddy 幫助理解佔位符,回溯,貪婪
[[email protected] ~]# echo "abc123" | grep -E --color 'a.*1.*'  
abc123
[[email protected] ~]# 

 匹對過程

a  #正則表示式第一個是字元a本身,這個直接匹對上

abc123  # 表示式a.*中的.*直接匹對後續的bc123,因為.*代表任意無限的字元組合,體現的貪婪機制

abc123  #回溯開始。因為1.*表示式中的1是字元本身,優先順序比上一個表示式a.*中的.*要高,所以要產生回溯。

abc12    #回溯要回溯到那個地點才行能?理論上要證明表示式的字元1是在內容的字元排序中的第一個出現,並且在與a.*所對應的字串中沒有1,那麼a.*所對應的字串就是佔有字元

abc12    #回溯開始。

abc1      #還不能證明表示式字元1是在內容的字元排序中的第一個出現,但記錄下第一次遇到字元1及位置

abc1      #回溯開始。

abc        #a.*中.*匹對成功,並且沒有字元1,故a.*的佔有字元產生(就是內容的abc)。也就是a.*可以退出這個正則匹對的舞臺了。那麼剩下的就到1.*上陣了

abc1      #1.*正則表示式中的字元1與內容的字元1匹對成功

abc123  #1.*完成匹對內容的123(數字123就是佔有字元),到此正則的比對完成。

零寬度主要是對字元 [ 組合 ] 位置搜尋方式的工作機制:核心就是讓正則表示式在那個位置開始匹對,後續工作交由對字元 [ 組合 ] 的排序搜尋方式接管

零寬度一般在一組表示式中有多選一時生效
[[email protected] ~]# echo "abc123" | grep -Eo --color '^(a..|1..)'
abc
[[email protected] ~]# echo "abc123" | grep -Eo --color 'a..|1..'   
abc
123
[[email protected] ~]# 

 分析過程

正則表示式 ^(a..|1..)中的 a.. 在內容的開始位置開始比較,1.. 依然在內容的開始位置開始比較,所以最終結果只能是 a.. 正則表示式能匹對abc123中的abc

正則表示式 a..|1.. 的原理基本上,同上述的對字元 [ 組合 ] 的排序搜尋方式差不多了。