Shell 正則表達(dá)式
1. Shell 正則表達(dá)式概述
1.1 正則表達(dá)式是什么
正則表達(dá)式 (regular expression)是一些具體有特殊含義的符號(hào),組合在一起的共同描述字符或字符串的方法,通俗來講正則為描述同一類事物的規(guī)則,例如我們生活中描述可以飛行的是事物,則滿足這條規(guī)則的可以是鳥,蝴蝶,也可以是飛機(jī)等。
在 Linux 系統(tǒng)中,正則表達(dá)式通常用來對(duì)字符或字符串來進(jìn)行處理,它是用于描述字符排列或匹配模式的一種語言規(guī)則。
1.2 為什么要用正則表達(dá)式
我們知道正則表達(dá)式是一個(gè)描述字符排列或模式匹配的規(guī)則,我們可以利用它來制定自己的規(guī)則,獲取到我們想要的結(jié)果等。在后續(xù)的 Shell 三劍客 grep/awk/sed Shell 的學(xué)習(xí)中,我們會(huì)結(jié)合正則表達(dá)式與這些命令進(jìn)行結(jié)合使用,來實(shí)現(xiàn)更強(qiáng)大的文本處理功能。正則表達(dá)式是我們 Shell 學(xué)習(xí)的核心也是難點(diǎn),在 Linux 中一切皆文件,多文件的處理可以覆蓋我們?nèi)粘9ぷ鞯?90%,所有熟練掌握正則表達(dá)式顯得尤為重要,在之后靈活配合其他命令可以非常方便的滿足我們的日常處理需求。
2. Shell 正則表達(dá)操作
在學(xué)習(xí)正則表達(dá)式的操作之前我們需要了解下 POSIX 及正則表達(dá)式的分類。
2.1 預(yù)備知識(shí)
2.1.1 POSIX
POSIX 稱為:Portable Operating System Interface(末尾增加 X 只是為了更流暢)的縮寫,后來被 IEEE 采納,由于在早期 Unix 系統(tǒng)時(shí)代各廠商發(fā)布不同版本的操作系統(tǒng),各版本之間存在著產(chǎn)品的差異,之后 IEEE 發(fā)布了一套 Unix 和類 Unix 系統(tǒng)工作方式規(guī)范,至此各常見遵循此規(guī)范來達(dá)到軟件兼容的效果。
2.1.2 正則表達(dá)式分類
正則表達(dá)式常見的有兩種分類:
- 基本正則表達(dá)式:Basic Regular Expression 又叫 Basic RegEx 簡(jiǎn)稱 BREs,在基本正則表達(dá)式中作用的元字符為:^ 、$、 . 、[、] 、* ;
- 擴(kuò)展正則表達(dá)式:Extended Regular Expression 又叫 Extended RegEx 簡(jiǎn)稱 EREs,其為在基本正則表達(dá)式上新增了 (、) 、{ 、} 、?、 + 、等元字符元字符,使得正則表達(dá)式更加簡(jiǎn)潔易用。
2.1.3 字符
在學(xué)習(xí)正則表達(dá)式前需要先學(xué)習(xí)字符。
- POSIX 字符
通用 POSIX 原字符如下:
[:alnum:] 字母數(shù)字[a-z A-Z 0-9]
[:alpha:] 字母[a-z A-Z]
[:blank:] 空格或制表鍵
[:cntrl:] 任何控制字符
[:digit:] 數(shù)字 [0-9]
[:graph:] 任何可視字符(無空格)
[:lower:] 小寫 [a-z]
[:print:] 非控制字符
[:punct:] 標(biāo)點(diǎn)字符
[:space:] 空格
[:upper:] 大寫 [A-Z]
[:xdigit:] 十六進(jìn)制數(shù)字 [0-9 a-f A-F]
- 特殊字符
在擴(kuò)展正則表達(dá)式中加上 \
則被認(rèn)為其具有特殊含義:
\w 匹配任意數(shù)字和字母,等效[a-zA-Z0-9_]
\W 和\w相反,等效[^a-zA-Z0-9_]
\b 匹配字符串開始或結(jié)束,等效\<和\>
\s 匹配任意的空白字符
\S 匹配非空白字符
2.1.4 區(qū)別
- 基本正則表達(dá)元字符:只有 ^$.*[];
- 擴(kuò)展正則表達(dá)式元字符:^$.[]*+(){}?|;
- 擴(kuò)展正則表達(dá)式對(duì)于 {m,n} 和 () 不需要再向基本正則表達(dá)式需要
\
來轉(zhuǎn)譯。
2.2 正則表達(dá)式操作
在 Linux 中正則表達(dá)式用來處理文本,在此我們使用 grep 工具對(duì)正則表達(dá)式進(jìn)行操作,grep 為文本過濾工具,在 grep 命令中默認(rèn)使用的時(shí)候基本正則表達(dá)式,可以使用選項(xiàng) -E
來開啟擴(kuò)展正則表達(dá)式,按照指定的正則表達(dá)式取出我們需求的內(nèi)容。
2.2.1 字符匹配
在字符匹配前需要先學(xué)習(xí)。
.
: 匹配任意單個(gè)字符,例如:
[root@master reg]# cat test.txt
she
sh
s1e
[root@master reg]# grep "s.e" test.txt
she
s1e
.
匹配必須為字母 s 與 e 中有任意單個(gè)字符。
[]
: 匹配指定中括號(hào)范圍內(nèi)的任意單個(gè)字符,例如:
[root@master reg]# cat test.txt
she
sh
s1e
[root@master reg]# grep "s[a-z]e" test.txt
she
[root@master reg]# grep "s[1-9]e" test.txt
s1e
[root@master reg]# grep "s[[:alnum:]]e" test.txt // 匹配字符或數(shù)字
she
s1e
[root@master reg]# grep "s[[:alpha:]]e" test.txt
she
[root@master reg]# grep "s[[:digit:]]e" test.txt
s1e
中括號(hào)內(nèi)可以利用元字符來表示。
[root@master reg]# cat test.txt
she
sh
s1e
[root@master reg]# grep "s[^[:digit:]]e" test.txt
she
[root@master reg]# grep "s[^a-z]e" test.txt
s1e
如上,匹配的元字符取反,也就是不包含匹配的內(nèi)容。
2.2.2 次數(shù)匹配
次數(shù)匹配用在指定的字符后面,表示指定匹配到前面的字符出現(xiàn)多少次。
*
: 匹配前面的字符任意次(0 次或無數(shù)次),例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "s*" test2.txt
ssssh
sheee
hell
如上匹配字符 s,0 次或多次。
\?
: 匹配前面的字符 0 次或 1 次,例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "s\?h" test2.txt
ssssh # 匹配最后的sh
sheee # 匹配sh
hell # 匹配h
[root@master reg]# grep -E "s?h" test2.txt
ssssh
sheee
hell
如上匹配 s 可以存在 0 次,或者存在 1 次之后需要有 h 字符,注意利用選項(xiàng) -E
開啟擴(kuò)展正則表達(dá)式,相較于基本正則表達(dá)式不需要 \
。
+
: 匹配前面的字符至少 1 次,例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "s\+h" test2.txt
ssssh # 匹配ssssh
sheee # 匹配sh
[root@master reg]# grep -E "s+h" test2.txt
ssssh
sheee
如上匹配 s 至少存在 1 次或無數(shù)次。
\{m\,}
: 匹配前面的字符至少 m 次(默認(rèn)工作在貪婪模式下,? 取消貪婪模式),例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "s\{1,\}" test2.txt
ssssh
sheee
[root@master reg]# grep -E "s{1,}" test2.txt
ssssh
sheee
[root@master reg]# grep "s\{2,\}" test2.txt
ssssh
[root@master reg]# grep -E "s{2,}" test2.txt
ssssh
匹配字符 s,最少 1 次。
\{,n}
: 匹配前面的字符最多 n 次(默認(rèn)工作在貪婪模式下,? 取消貪婪模式),例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "s\{,2\}" test2.txt
ssssh
sheee
hell
[root@master reg]# grep -E "s{,2}" test2.txt
ssssh
sheee
hell
匹配字符 s,最多 2 次。
\{m,n\}
: 匹配前面的字符至少 m 次,至多 n 次,例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "s\{1,2\}" test2.txt
ssssh
sheee
[root@master reg]# grep -E "s{1,2}" test2.txt
ssssh
sheee
匹配字符 s,1-2 次之間。
.*
: 匹配任意字符任意次數(shù)。
2.2.3 位置錨定
^
: 行首錨定,用于模式最左邊,例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "^s" test2.txt
ssssh
sheee
匹配以 s 開頭的行。
$
: 行尾錨定,用于模式最右邊,例如:
[root@master reg]# cat test2.txt
ssssh
sheee
hell
[root@master reg]# grep "h$" test2.txt
ssssh
匹配以 h 結(jié)尾的行。
\<
或\b
: 錨定詞首,用于單詞模式左側(cè),例如:
[root@master reg]# cat test2.txt
go root user
root:shell;gousers
hellorootgouser
[root@master reg]# grep "\<ro" test2.txt
go root user
root:shell;gousers
[root@master reg]# grep "\bro" test2.txt
go root user
root:shell;gousers
可以看到此刻匹配是以單詞模式,沒有匹配 helloroot。
\>
或\b
: 錨定詞尾,用于單詞模式右側(cè),例如:
[root@master reg]# grep "gouser\b" test2.txt
hellorootgouser
[root@master reg]# grep "gouser\>" test2.txt
hellorootgouser
2.2.4 分組引用
()
分組:將一個(gè)或多個(gè)字符當(dāng)成一個(gè)整體來進(jìn)行后續(xù)處理;1…數(shù)字
引用:從左側(cè)起,引用第一個(gè)左括號(hào)以及與之匹配右括號(hào)之間的模式所匹配到的字符,后向引用,例如:
grep -E "(root).*\1" /etc/passwd
利用 () 將 root 引用起來,后面利用數(shù)字 1 引用。
3. 實(shí)例
1.顯示/etc/init.d/functions文件中以大小s開頭的行
grep '^[Pp]' /etc/init.d/functions
2.顯示/etc/passwd文件中以/bin/bash結(jié)尾的行
grep "/bin/bash$" /etc/passwd
3.顯示/etc/passwd文件中ID號(hào)最大用戶的用戶名
sort -t: -k3 -n /etc/passwd |tail -1 |cut -d: -f1
4.如果root用戶存在,顯示其默認(rèn)的shell程序
id root && grep '^\<root\>' /etc/passwd |awk -F: '{print $NF}'
5.找出/etc/passwd中的兩位或三位數(shù)
grep -o "[0-9]\{2,3\}" /etc/passwd
6.顯示/etc/rc.d/rc.sysinit文件中,至少以一個(gè)空白字符開頭的且后面存非空白字符的行:
grep '^[[:space:]]\+[^[:space:]]' /etc/rc.d/rc.sysinit
7.找出"netstat -tan"命令的結(jié)果以"LISTEN"后跟0,1或多個(gè)空白字符結(jié)尾的行
netstat -tan|grep 'LISTEN[[:space:]]*$'
8.如果root用戶登錄了系統(tǒng),就顯示root用戶在線,否則說明未登錄
w |grep '^\<root\>'>/dev/null && echo "root在線"|| echo "root未登錄"
9.找出/etc/rc.d/init.d/functions文件中某單詞后面跟一對(duì)小括號(hào)的行
grep '[[:alpha:]]*()' /etc/rc.d/init.d/functions
10.使用echo輸出一個(gè)路徑,使用egrep取出基名
echo /tmp/tmp1/vmstat.8.gz |grep -E -o '[^/]+/?$'|cut -d/ -f1
11.匹配PPID開頭,行中又再次出現(xiàn)PPID的內(nèi)容。/etc/init.d/functions
grep -E "(PPID).*\1" /etc/init.d/functions
12.利用awk找出/etc/ssh/sshd_config內(nèi)出過空行與以#開頭的行
grep -v -E '^#|^$' /etc/ssh/sshd_config
4. 注意事項(xiàng)
- 正則表達(dá)式中基本正則表達(dá)式與擴(kuò)展正則表達(dá)式配合其它操作,能夠千變?nèi)f化,非常靈活,根據(jù)不同場(chǎng)景可以進(jìn)行正向匹配和反向匹配;
- 正則表達(dá)式配合命令通常為三劍客 grep/awk/sed 等,后期靈活進(jìn)行組合達(dá)到事半功倍的效果;
- 我們?cè)谖谋灸J狡ヅ涞臅r(shí)候可以考慮使用擴(kuò)展的正則表達(dá)式,從而避免使用過多的轉(zhuǎn)義字符。
5 小結(jié)
正則表達(dá)式可謂 Shell 中的精華,其實(shí)在其他語言中也很通用,需要進(jìn)行勤加練習(xí)才能達(dá)到熟練掌握,注意區(qū)分基本正則表達(dá)式與擴(kuò)展正則表達(dá)式的語法區(qū)別,配合其他工具靈活運(yùn)用。在不同場(chǎng)景可以利用命令選項(xiàng)配合使用,在后期 Shell 腳本三劍客中也會(huì)頻繁出現(xiàn)正則表達(dá)式,攻克正則表達(dá)式這個(gè)難關(guān),Shell 腳本編程就已經(jīng)事半功倍。