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