正則表達式(Regular Expression,通常簡稱為 regex 或 RE)是一種表達方式,可以用它來查找匹配特定准則的文本。在許多編程語言中都有用到正則表達式,常用它來實現一些複雜的匹配。這裡簡單介紹一下 shell 中常用到的一些正則表達式。 一、什麼是正則表達式 正則表達式是對字元串進 ...
正則表達式(Regular Expression,通常簡稱為 regex 或 RE)是一種表達方式,可以用它來查找匹配特定准則的文本。在許多編程語言中都有用到正則表達式,常用它來實現一些複雜的匹配。這裡簡單介紹一下 shell 中常用到的一些正則表達式。
一、什麼是正則表達式
正則表達式是對字元串進行操作的一種邏輯公式,即用事先定義好的的一些特定字元以及這些特定字元的組合,組成一個有一定規則的字元串(Regular Expression),使用這個有一定規則的字元串來表達對字元串的一種過濾邏輯。正則表達式被廣泛應用於Linux和許多其他編程語言中,而且不論在哪裡,其基本原理都是一樣的。
從根本上來看,正則表達式是由兩個基本組成部分所建立:一般字元與特殊字元。一般字元是指沒有任何特殊意義的字元;特殊字元,常稱為元字元 (metacharacter),或 meta 字元,顧名思義,就是指那些有特殊意義的字元,當然在某些情況下,特殊字元也可被視為一般字元(使用轉義符 \ 進行轉義)。
POSIX 有兩種風格的正則表達式,基本正則表達式(BRE)和擴展正則表達式(ERE)。這兩種風格的正則表達式在一些字元含義上有細微的差距。以常用的 grep 指令來說,grep 指令預設支持的是 BRE,若要使用 ERE 進行匹配,可以使用 -E 選項,接下來的例子中均使用 grep 指令來演示正則表達式的使用。
二、基本正則表達式
2.1 常用 meta 字元列表
字元 | BRE/ERE | 含義 |
. | BRE&ERE | 匹配任意單個字元(除字元串結束符 NUL) |
^ | BRE&ERE | 匹配行首,如 ^abc,匹配以 abc 開頭的字元串 |
$ | BRE&ERE | 匹配行尾,如 abc$,匹配以 abc 結尾的字元串 |
* | BRE&ERE | 匹配 0 個或任意多的單個字元,前置字元可以是正則表達式 |
+ | ERE | 匹配前面正則表達式的 1 個或多個實例 |
? | ERE | 匹配前面正則表達式的 0 個或 1 個實例 |
[...] | BRE&ERE |
方括弧表達式,匹配方括弧內的任一字元,常配合 - 符使用,表示匹配一個連續的範圍。 ^ 字元 作為方括弧內的第一個字元表示匹配不在方括弧內的任意字元 |
- | BRE&ERE | 連字元,在方括弧表達式中使用,表示連續字元的範圍(範圍會因 locale 而有所不同,因此不具可移植性) |
{n,m} | ERE | 區間表達式,表示匹配在它前面的字元 n 到 m次。其中,n 與 m 的值必須介於 0-RE_DUM _MAX(含)之間,後者最小值為255 |
{n} | ERE | 表示匹配在這之前的字元 n 次 |
\{n,m\} | BRE | 功能同 {n,m} |
\{n\} | BRE | 功能同{n} |
\ | BRE&ERE | 轉義符 |
() | ERE | 匹配位於方括弧括起來的正則表達式群 |
\( \) | BRE |
將\( 與 )\ 之間的模式保存在特殊的“保留空間”中,最多可以存儲9個,可以通過後續的轉義序列 \n 來匹配保留空間中的模式 |
\n | BRE | 與 \( \) 結合起來使用,\1匹配第一個子模式、 \2匹配第二個,最多到 \9 |
| | ERE | 匹配位於 | 符號前或後的正則表達式 |
接下來,筆者將使用 grep 命令對 /etc/passwd 文件進行查找匹配操作:
1)匹配一般字元:
[tongye@localhost ~]$ grep root /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin
2)使用點字元 " . " 匹配任意字元:
[tongye@localhost ~]$ grep r..t /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
3)使用星號字元 " * " 或問號字元 " ? "匹配0個或多個字元:
[tongye@localhost ~]$ grep roo* /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin
grep roo* /etc/passwd 命令將在 /etc/passwd 中匹配 ro ,後面可以接 0 個或多個 o。在 ERE 風格下,使用的是 ? 符號來達到和 * 號一樣的效果:
[tongye@localhost ~]$ grep -E roo? /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin
4)使用加字元 " + " 匹配1個或多個字元:
[tongye@localhost ~]$ grep -E roo+ /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin [tongye@localhost ~]$ grep -E ro+ /etc/passwd root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin
使用 + 字元可以匹配在其前面的 1 個或多個字元,與 * 字元有些許的差別,另外, + 字元實在 ERE 風格下使用的,故需要使用 grep 命令的 -E 選項
5)使用 ^ 匹配行首,$匹配行尾:
[tongye@localhost ~]$ grep ^t /etc/passwd tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin tongye:x:1000:1000:tongye:/home/tongye:/bin/bash [tongye@localhost ~]$ grep ^t.*h$ /etc/passwd tongye:x:1000:1000:tongye:/home/tongye:/bin/bash
.* 結合在一起表示匹配零個或多個任意字元,與 ^ 和 $ 結合起來使用的話就可以匹配一個指定開頭和結尾的字元串了
6)使用方括弧表達式匹配括弧內的任一字元:
[tongye@localhost ~]$ grep [Nn]et /etc/passwd systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
結合 - 字元使用,可以表示匹配一個範圍內的任一字元,如 [0-9] 表示匹配 0-9 中的任意一個數字、[a-z] 表示匹配一個小寫字母、[A-Z] 表示匹配一個大寫字母:
[tongye@localhost ~]$ grep [a-z]c /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin abrt:x:173:173::/etc/abrt:/sbin/nologin
結合 ^ 字元使用,表示取反
[tongye@localhost ~]$ grep [^a-z]c /etc/passwd tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin chrony:x:998:996::/var/lib/chrony:/sbin/nologin
這裡表示匹配一個非小寫字母的字元後接一個字元 c 的字元串。註意,^ 放在方括弧裡面表示反向含義,放在方括弧外面則表示的是匹配行首。
7)使用 {n.m} 區間表達式來匹配指定的次數:
這個表達式可以用來匹配指定的次數,其中 {n,m} 表示匹配在其前面的字元 n 到 m次,{n,} 表示至少匹配 n 次,{,m} 表示最多匹配 m 次,而 {n} 則是精準匹配 n 次。在 BRE 中,使用的是 \{n,m\} 的形式來實現相同的功能。n 與 m 的值必須介於 0 至 RE_DUP_MAX(包含這個值)之間,後者的最小值為255
[tongye@localhost ~]$ grep 0'\{3\}' /etc/passwd tongye:x:1000:1000:tongye:/home/tongye:/bin/bash [tongye@localhost ~]$ grep -E 0{3} /etc/passwd tongye:x:1000:1000:tongye:/home/tongye:/bin/bash
8)使用 \( \) 保存已匹配的字元,並通過 \n 來引用已保存的匹配字元串
使用 \( \) 會先匹配括弧中的字元串,然後將匹配到的字元串保存在由正則表達式解析器預定義好的叫做寄存器的變數中,其編號從1到9,也就是說最多可以保存9組字元串,使用 \n 可以取出所保存的字元串,其中 n 為1到9,分別對應9個寄存器的值。
[tongye@localhost ~]$ grep '\(operator\).*\1' /etc/passwd operator:x:11:0:operator:/root:/sbin/nologin
關於這個表達式還有一個有意思的用法:
[tongye@localhost ~]$ grep '^\(.\).*\1$' /etc/passwd nobody:x:99:99:Nobody:/:/sbin/nologin
如上,正則表達式 ^\(.\).*\1$ 將匹配一個行首字元和行尾字元相同的字元串。
2.2 POSIX 方括弧表達式
為了配合非英語的環境,POSIX 標準強化其字元集範圍的能力 (如 [a-z]),以匹配非英文字母字元。POSIX 使用方括弧表達式 [...] 來表示一個範圍值,在方括弧表達式里,除了字面上的字元外(a、b、c等),另有額外的組成部分,包括:
1)字元集:以 [: ... :] 將關鍵字組合括起來的 POSIX 字元集,關鍵字描述各種不同的字元集;
2)排序符號:排序符號將多個字元序列視為一個單位(如,locale 中將 ch 這兩個字元視為一個單位),它使用 [. 與 .] 將字元組合括起來,在系統所使用的特定 locale 上各有其定義;
3)等價字元集:等價字元集列出的是應視為等值的一組字元,它由取自於 locale 的名字元素組成,以 [= 與 =] 括住。
下表是 POSIX 字元集列表:
類別 | 匹配字元 |
[:alnum:] | 數字字元 |
[:alpha:] | 字母字元 |
[:blank:] | 空格與定位符 |
[:cntrl:] | 控制字元 |
[:digit:] | 數字字元 |
[:graph:] | 非空格字元 |
[:lower:] | 小寫字母字元 |
[:upper:] | 大寫字母字元 |
[:space:] | 空白符 |
[:print:] | 可顯示的字元 |
[:punct:] | 標點符號字元 |
[:xdigit:] | 十六進位數字 |
需要註意的是,上述字元集也是要放到方括弧表達式中去的,因此一般會出現類似 [[:alpha:]] 的表達式。
舉個例子:
[tongye@localhost ~]$ grep [[:upper:]] /etc/passwd ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin dbus:x:81:81:System message bus:/:/sbin/nologin polkitd:x:999:998:User for polkitd:/:/sbin/nologin tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
該正則表達式匹配所有的大寫字母。
參考資料:
《Linux程式設計 第四版》
《Shell 腳本學習指南》
《UNIX/Linux/OS X 中的 Shell 編程 第四版》