Linux grep 命令

sparkdev發表於2019-08-05

Grep 是 Global Regular Expression Print 的縮寫,它搜尋指定檔案的內容,匹配指定的模式,預設情況下輸出匹配內容所在的行。注意,grep 只支援匹配而不能替換匹配到的內容。

基本語法

語法格式:
grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]

grep 支援不同的匹配模式,比如預設的 BRE 模式,增強型的 ERE 模式,還有更強悍的 PRE 模式。普通情況下使用預設的 BRE(basic  regular  expression) 模式就可以了,這種方式的特點是支援的正規表示式語法有限。如果需要更進一步的正規表示式語法支援,可以使用 ERE(extended regular expression) 模式。如果要使用複雜的正規表示式語法,可以使用 PRE 模式,它支援 Perl 語言的正規表示式語法。

常用選項:
--help
-V, --version
-G, --basic-regexp        BRE 模式,也是預設的模式
-E, --extended-regexp  ERE 模式
-P, --perl-regexp           PRE 模式
-F, --fixed-strings          指定的模式被解釋為字串
-i 忽略大小寫
-o 只輸出匹配到的部分(而不是整個行)
-v 反向選擇,即輸出沒有沒有匹配的行
-c 計算找到的符號行的次數
-n 順便輸出行號

常見用例

遞迴目錄中的所有檔案
預設情況下 grep 會匹配指定定檔案中的內容,如果我們指定了一個目錄,grep 則直接罷工:

使用選項 -R, -r, --recursive 會遞迴指定目錄下的所有檔案,並匹配其內容:

$ grep -r 'world' ~/projects/

透過 -d recurse 選項可以實現同樣的功能:

$ grep 'world' -d recurse ~/projects/

一般情況下使用下面的命令(-n 顯示行號,-w 表示匹配全詞):

$ grep -rnw 'path' -e 'pattern'

在遞迴的過程中只輸出匹配內容所在的檔名稱
如果我們只想檢視匹配到的內容所在檔案的名稱,可以同時使用 r 和 -l, --files-with-matches 選項:

$ grep -rl email /home/nick/projects/bash/.git

在遞迴的過程中排除某些目錄
可以在應用選項 r 的同時應用 --exclude-dir 選項來排除一些目錄(注意,這裡設定的也是正規表示式):

$ grep -r --exclude-dir='.git' 'email' .

還可以同時指定多個表示式:

$ grep -r --exclude-dir={.git,xgit} 'email' .

在遞迴的過程中排除指定的檔案
可以在應用選項 r 的同時應用 --exclude 選項來排除一些檔案(注意,這裡採用的是 GLOB模式):
$ grep -r --exclude=*.txt 'email' .

不區分大小寫
Grep 預設的匹配規則區分字元的大小寫,使用選項 -i (小寫字母i), --ignore-case 會在匹配中忽略字元大小寫:

$ grep -i 'hello' email1

只輸出匹配到的部分(而不是整個行)
Grep 預設會輸出匹配到的內容所在的整個行,使用選項 -o, --only-matching 則只輸出匹配到的內容:

$ echo "abc 123 test" | grep -o '[0-9]\{1,3\}'

輸出的結果為:123
在需要把匹配的內容存入變數時 -o 選項非常有用,比如下面的示例把從檔案中匹配到的 IP 地址儲存在變數 ip 中:

ip=$(grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' file.log)
if test -z "${ip}"; then
    exit 1
fi
echo ${ip}

把匹配條件當成一個字串
有時候我們想要匹配一個固定的字串,但是其中包含了特殊字元,比如:

$ grep '.*' email1.txt 

這樣的條件會返回檔案中的每一行內容,這不是我們想要的。可以透過轉義符來解決這個問題:

$ grep '\.\*' email1.txt

當然,我們還可以透過選項 F 來優美的解決這個問題,此時指定的條件會被當成一個字串來匹配:

$ grep -F '.*' email1.txt

同時輸出匹配行後的 n 行
使用選項 -A n 可以輸出匹配行後的 n 行,結合 ps 命令,可以用來查詢某個程式的子程式。下面的例子透過 -A 1 輸出容器執行的程式(bash):

$ ps fxa | grep -A 1 docker

同時輸出匹配行前後的行
有時我們想要看到匹配行的前後行的內容,使用選項 -C n 可以實現這個功能,比如下面的命令會同時輸出匹配行前後 1 行的內容:

$ grep -C 1 'grey' email1.txt

在輸出中顯示行號
使用選 -n 可以在輸出中顯示行號:

統計匹配到的行的數量
使用選項 -c 可以統計匹配到的行的數量:

反轉匹配條件
如果我們想要獲取正規表示式沒有匹配到的行,可以使用選項 -v, --invert-match

$ grep -v '^[a-zA-Z].*' email1.txt

上面的輸出為不是以字母開頭的行。

只匹配完整的行
如果我們只對一個完整的行感興趣,可以使用選項 -x, --line-regexp。這樣會忽略那些包含在行中的內容:

從檔案中讀取正規表示式
如果正規表示式太長,或者是需要指定多個正規表示式,可以把它們放在檔案中,然後使用 選項 -f FILE, --file=FILE 來指定這個檔案。如果指定了多個正規表示式(每行一個),任何一個匹配到的結果都會被輸出:

使用預定義的命名字元類

$ grep '[[:digit:]]\{1,3\}' email1.txt

BRE(basic  regular  expression)

語法 說明 解釋
. 匹配一個任意的字元 在 [] 中 . 號並不是元字元
^ 行的起始 ^ 和 $ 匹配的是一個位置而不是具體的文字
$ 行的結束 ^ 和 $ 匹配的是一個位置而不是具體的文字
* 匹配 0 次或多次 不匹配上一次表示式,匹配上一次或匹配多次,並生成所有可能的匹配
匹配儘可能多的次數,如果實在無法匹配,也不要緊
[] 匹配若干個字元之一 又叫字元組、字元類,比如 [0-9]、[a-z]、[A-Z]
只有在字元組內部 - 才是元字元,表示一個範圍
[^...] 排除型字元組 字元組以 ^ 開頭,它會匹配一個任何未列出的字元
\? 可選元素 在 BRE 中需要使用轉義符 \
出現一次或者不出現
\+ 匹配 1 次或多次 在 BRE 中需要使用轉義符 \
匹配前面表示式的至少一個搜尋項
匹配儘可能多的次數,如果實在無法匹配,也不要緊
 \{min,max\}  量詞區間 在 BRE 中需要使用轉義符 \
 \|  或(多選結構) 在 BRE 中需要使用轉義符 \
bob\|nick 能夠同時匹配其中任意一個的正規表示式,此時的子表示式被稱為 "多選分支"
多選結構可以包括很多字元,但不能超越括號的界限
 \(\)  分組 在 BRE 中需要使用轉義符 \
括號能夠 "記住" 它們包含的子表示式匹配的文字
反向引用(backreference)是正規表示式的特性之一,它允許我們匹配與表示式先前部分匹配的同樣的文字
 \<\>  單詞分界符 在 BRE 中需要使用轉義符 \
< 和 > 本身並不是源字元,只有它們與反斜線結合時才具有單詞分界符的含義
 \  轉義符 如果需要匹配的某個字元本身就是元字元,就需要使用轉義符
  命名的字元類  命名的預定義字元類 [[:upper:]]   [A-Z]
[[:lower:]]   [a-z]
[[:digit:]]   [0-9]
[[:alnum:]]   [0-9a-zA-Z]
[[:space:]]   空格或 tab
[[:alpha:]]   [a-zA-Z]

ERE(extended regular expression)

與 BRE 相比 ERE 最大的優點是支援更多的元字元,也就是在使用這些字元時不需要 \ 了。比如上面 BRE 中使用的 \ 符可以全部去掉。

 

參考:
grep man page
grep gnu page
Basic (BRE) and extended (ERE) regular expression
Overview of basic regular expression syntax
Overview of extended regular expression syntax
Grep usage examples
14 grep Command Examples for Linux Users
How to Grep for Text in Files
Using Grep & Regular Expressions to Search for Text Patterns in Linux

相關文章