本文介紹基礎正則表達式,沒有示例(),只有總結和"解惑",如果想學習更強大、更完整的正則,可以參考:Perl正則超詳細教程,grep -P
、ack都支持Perl正則,且很多需要使用到正則的服務軟件一般都采用PCRE(如httpd、nginx、haproxy、proxysql),它和Perl正則幾乎完全一致,作為運維人員,我覺得是有必要學上一學的。
基礎正則
元字符:
.
:匹配任意單個字符,但不能匹配換行符\n
*
:匹配前面那個字符0或多次?
:匹配前面那個字符0或一次+
:匹配前面那個字符1次以上{M,N}
:匹配前面那個字符至少M,最多N次{M,}
:匹配前面那個字符至少M次,最多無限制{,N}
:匹配前面那個字符最多N次(最少當然是0次)。注意,perl正則不支持這種方式{M}
:匹配前面那個字符正好M次- 錨定:錨定的意思是匹配位置,而非匹配字符實體
^
:匹配行首位置,注意匹配的是位置,不是字符$
:匹配行尾位置,注意匹配的是位置,不是字符
特殊且常用的的組合正則表達式:
^$
:它表示匹配空行.*
:匹配任意長度的任意字符,但不能匹配換行符。真正的匹配任意長度的任意字符,見下面
需要解釋清楚的是這些量詞(也就是上面匹配的次數元字符)的特殊性:當使用了匹配多次的量詞時(如匹配3-5次的{3,5}
),且量詞前面的字符有多種可能性(如中括號序列[abc]
),那么量詞的次數可以作用於任一字符。有些不好理解,但看示例就知道了:
[abc]{3,5} # 表示abc任意字符都可以出現,比如全是a,或者ab同時出現,但總的出現次數為3-5次
.* # 表示任一字符(除換行符),可以任意出現任意次數,它不表示a之后就必須全是a
.+ # 表示任一字符(除換行符),可以任意出現至少一次,它不表示a之后就必須全是a
另外,.
無法匹配換行符。可能你不太理解為什么需要匹配換行符,它主要用在:
- 多行模式。例如sed的多行模式下,要跨行匹配需要手動指定"\n",如
/^a.*\nb.*/
- 明確指定了行分隔符為非"\n"的情況。例如awk可以使用RS變量指定輸入行分隔符
中括號
中括號表示的是匹配任意一個,一般它和字符集的排序規則有關,不同工具采取的排序規則可能也不一樣。
[abcd...]
:匹配中括號內的任意一個字符[^abcd...]
:拒絕匹配中括號內的任意字符[a-z]
:匹配字母a到z[A-Z]
:匹配字母A到Z[0-9]
:匹配0-9,也就是匹配數字
關於字母的排序:
- perl中,A-Z排在a的前面,所以[A-z]表示所有大小寫字母
- grep中,A-Z排在z的后面,所以[a-Z]表示所有大小寫字母
- 還有些工具中,大小寫的排序規則是aAbBcC...zZ,所以[a-C]表示aAbBcC共6個字母
字符類
是專門命名的中括號序列;除了字符類,還有等價類、排序類,但基本用不上,只用字符類。
[:alpha:]
:匹配字母,等價於[a-zA-Z]
[:digit:]
:匹配數字,等價於[0-9]
[:xdigit:]
:匹配十六進制數,等價於[0-9a-fA-F]
[:upper:]
:匹配大寫字母,等價於[A-Z]
[:lower:]
:匹配小寫字母,等價於[a-z]
[:alnum:]
:匹配數字或字母,等價於[0-9a-zA-Z]
[:blank:]
:匹配空白,包括空格和制表符[:space:]
:匹配空格,包括空格、制表符、換行符、回車符等各種類型的空白[:punct:]
:匹配標點符號。包括:! ' " ` # $ % & ( ) * + , . - _ / : ; < = > ? @ [ \ ] ^ { | } ~
[:graph:]
:繪圖類。包括:大小寫字母、數字和標點符號。等價於[:alnum:]
+[:punct:]
[:print:]
:打印字符類。包括:大小寫字母、數字、標點符號和空格。等價於[:alnum:]
+[:punct:]
+space[:cntrl:]
:控制字符類。在ASCII中,這些字符的八進制代碼從000到037,還包括177(DEL)
需要注意的是,通常字符類在真正使用過程中,會再加上一個中括號,例如[[:alpha:]]
。之所以如此,是因為這些字符類只是一種命名好的字符集合。例如[:lower:]
對應的字符集合是a-z,而不是[a-z]
,所以要想讓其表示這些命名字符類中的任一字符,需要再加上一層括號[[:lower:]]
,它才等價於[a-z]
。可能會更有助於理解使用字符類的時候為什么要加兩個中括號的例子是[^[:lower:]]
,它表示不包含任何小寫字母。
反斜線序列
不同的工具,同一工具不同的版本,支持的反斜線序列能力不同。以下列出了部分常見序列。
以下所說的單詞,一般來說只包含數字、字母和下划線,即[_0-9a-zA-Z]
。
以下幾種反斜線序列,基本上所有工具都支持:
\b
:匹配單詞邊界處的空字符\B
:匹配非單詞邊界處的空字符\<
:匹配單詞開頭處的空字符\>
:匹配單詞結尾處的空字符\w
:匹配單詞構成部分,等價於[_[:alnum:]]
\W
:匹配非單詞構成部分,等價於[^_[:alnum:]]
以下幾種,有些工具不支持,但perl都支持:
\s
:匹配空白字符,等價於[[:space:]]
\S
:匹配非空白字符,等價於[^[:space:]]
\d
:匹配數字,等價於[0-9]
\D
:匹配非數字,等價於[^0-9]
由於元字符.
默認無法匹配換行符,所以需要匹配換行符的時候,可以使用特殊組合[\d\D]
來替換.
,換句話說,如果想匹配任意長度的任意字符,可以換成[\d\D]*
,當然,前提是必須支持\d
和\D
兩個反斜線序列。
分組捕獲和反向引用
基礎正則中,使用括號可以對匹配內容進行分組並暫時保存,分組后會有分組編號,可以使用反斜線加編號\N
的方式反向引用這些分組。
分組編號的方式是從左向右計算括號數,無論如何嵌套,第一個左括號對應的分組一定是編號1,用\1
來引用,第二個左括號對應的分組一定是編號2,用\2
來引用,依此類推。
例如grep的分組捕獲:匹配兩個連續相同的字母。
echo "abcddefg" | grep -E "(.)\1"
可以認為分組就是變量賦值的過程。例如,上面示例的匹配過程如下:
1.匹配第一個字母a,放進分組,即賦值給變量(假設變量名為$1),即$1="a"
,再繼續執行正則表達式匹配過程的反向引用,它引用的是$1,於是表示第一個字母a后面還要是字母a,於是匹配失敗。
2.匹配第二個字母b,放進分組,即$1="b"
,再匹配后一個字母,於是匹配失敗。
3.字母c同樣如此。
4.匹配字母d,放進分組,即$1="d"
,再匹配后一個字母,發現匹配成功,於是$1被保存下來。
5.已經匹配成功,於是結束。
對於只使用基礎正則的工具來說,一般都只能引用\1
到\9
共9個反向引用,最多自己額外提供一個所有表示匹配內容的反向引用(例如sed提供的&
)。對於超出10個的分組,使用基礎正則的工具一般來說是無能為力的。
再者,基礎正則僅僅只是將分組匹配到的內容捕獲,在正則操作結束后就丟失。但對於一門完整編程語言來說,這遠遠不夠,幾乎所有編程語言(如perl/java/python等)都會將正則的分組匹配內容保存為變量,使得可以在正則結束之后再次引用甚至修改它們。例如上面例子中分組捕獲的字母d,如果換成perl,即使在這個匹配過程結束后,還是可以去引用這段分組。
二選一
pattern1 | pattern2
:匹配豎線左邊,或者匹配豎線右邊都算匹配成功
關於二選一的結構,幾點需要說明:
- 因為豎線元字符的優先級很低,所以
ab|cd
匹配的是"ab"或"cd",而不是abd或acd。 - 成功匹配了左邊,就不會再去對右邊進行匹配。
- 反向引用失敗問題:豎線將兩邊的分組隔開,右邊的永遠無法反向引用左邊的分組
在二選一結構種,兩個反向引用問題的典型例子:
例如a(.)|b\1
將無法匹配"ba",因為評估了左邊就不會評估右邊。
例如([ac])e\1|b([xyz])\2t
的左邊能匹配aea或cec,但不能匹配cea或aec,右邊能匹配bxxt或byyt或bzzt。但如果將\2
換成\1
,即([ac])e\1|b([xyz])\1t
,將無法匹配b[xyz]at或b[xyz]ct,因為第一個分組括號在左邊,無法參與右邊的正則評估。