grep技術要點
1) 工作模型(3 件事)
- 輸入:從文件或標準輸入(
-
)讀入,一次按“行”處理(除非用-z
改成以 NUL 作為“行”分隔)。 - 匹配:把每一行拿去和模式(pattern)比對。模式可以是正則(BRE/ERE/PCRE)或純文本(
-F
)。 - 輸出:默認打印匹配到的整行;用
-o
可只打印匹配片段;用-l/-L/-c
改為打印文件名或計數。
退出碼(腳本很有用):匹配到返回0
,未匹配返回1
,出錯(且沒-q
)返回2
。
2) 模式種類 & 選型思路
-G
(BRE,默認):基礎正則。+ ? | ()
在 BRE 里不是元字符,需轉義:\+ \? \| \(\)
.-E
(ERE):擴展正則,+ ? | ()
直接可用;日常首選,等價于歷史上的egrep
。-F
(固定字符串):把模式當“純文本”,不解正則;最快、最穩,適合日志多關鍵詞查找。-P
(Perl/PCRE):支持高級正則(如\b
、斷言(?<=x)
、懶惰量詞等)。有時未編進發行版,或在極大文件上稍慢——僅當確實需要這些能力再用。
通用小抄(ERE 語義為例)
^
行首;$
行尾;.
任意單字符;*
前導重復 ≥0 次;+
≥1 次;?
0或1;{m,n}
次數;[]
字符類;[^]
取反;()
分組;|
或;[[:alpha:]] [[:digit:]] [[:space:]]
等POSIX 類對多語言更穩。
整詞:-w
(按“字母/數字/下劃線”邊界)或-P '\bword\b'
。
多模式:多次-e
,或-f pattern_file
(每行一個模式)。
3) 遞歸與路徑篩選
-r
遞歸;-R
遞歸且跟隨符號鏈接。默認無文件時,遞歸會把“當前目錄”當作輸入。- 只搜特定文件:
--include='*.{c,h}'
;排除:--exclude='*.min.js'
、--exclude-dir={.git,node_modules,build}
。 - 搭配
-n
(行號)、-H/-h
(是否加文件名前綴)提升可讀性。
4) 輸出控制與上下文
- 只要文件名:
-l
(有匹配的文件);-L
(沒有匹配的文件)。 - 只要數量:
-c
;只要命中片段:-o
(常與-h
連用,便于做統計)。 - 上下文:
-A NUM
(后文)、-B NUM
(前文)、-C NUM
(兩側),或簡寫-NUM
。 - 限流:
-m NUM
(每個文件匹配到 NUM 行就停),提高速度。 - 顏色:
--color=auto
(或--colour
)。管道里可能被判為非 TTY,顏色會自動關閉。
5) 大文件/二進制/編碼細節
- 二進制檢測觸發時會出現“Binary file matches”。
- 當你只是想按文本看:用
-a
或--binary-files=text
。 - 想忽略二進制:
-I
(等價--binary-files=without-match
)。
- 當你只是想按文本看:用
-z
:把 NUL 當“換行”,配合find -print0 / xargs -0
進行安全文件名流水線;-Z
輸出文件名后加 NUL。- 速度技巧:
LC_ALL=C grep -F ...
(字節序比較、不開多字節;非常快,但不適合需要本地化字符類/大小寫折疊的場景)。
6) 易踩的坑(超重要)
- 一定要正確引用模式:在 shell 里
* ? () | $ \
都可能被解釋,用單引號包裹模式最穩:'foo.*bar'
。 - BRE/ERE 轉義差異:在
-G
下+ ? | ()
需轉義;改成-E
通常更直觀。 -P
可用性:有些系統未啟用 PCRE;腳本里用-P
前先確認環境。- 遞歸與鏈接:
-R
可能跟隨循環鏈接導致慢或“走不出來”;需要時用--exclude-dir
限制。 -w
的“詞邊界”定義:只把字母/數字/下劃線當詞字符;含連字符或點號的“詞”不適合-w
,改用-P '\b'
或顯式邊界。- 大小寫與本地化:
-i
在多字節/本地化下可能和你以為的不完全一致;要“字節級”行為就LC_ALL=C
。
7) 選型口訣
- 純文本多關鍵詞:
-F
或-Ff
(最快)。 - 一般正則:優先
-E
(少轉義,讀寫性好)。 - 需要斷言/詞邊界:
-P
(確認環境支持)。 - 遞歸找代碼:
-Rni --include=… --exclude-dir=…
。 - 只要文件名/計數:
-l / -L / -c
。 - 抽取值:
-o
(必要時-P
斷言)。 - 大文件提速:
-m1
(命中即停)、LC_ALL=C
、縮小--include/--exclude
范圍。
命令詳解
主要用法如下:
grep [OPTION]... PATTERNS [FILE]...
要點:命令基本形式。PATTERNS
是要匹配的模式(可以是正則或字符串);FILE
是一個或多個文件名。
示例:
grep 'error' /var/log/syslog
Search for PATTERNS in each FILE.
要點:對每個文件逐行檢查模式,默認輸出匹配到的整行(除非用 -o
或其它選項改變輸出)。
示例:
grep 'TODO' main.c util.c
Example: grep -i 'hello world' menu.h main.c
要點:示例說明 -i
(忽略大小寫)如何使用。
示例(同上):
grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.
要點:一個模式文件或多行模式串中每行可視為一個獨立模式;任一行匹配即為命中。
示例:
假設 patterns.txt
內容為:
ERROR
WARN
FATAL
則:
grep -Ff patterns.txt app.log
會查找包含任意一行模式的日志行。
模式選擇與解釋
-E, --extended-regexp PATTERNS are extended regular expressions
要點:使用擴展正則(ERE)。在 ERE 中,+ ? | () {}
等元字符無需反斜杠轉義,寫法更直觀。
示例:
grep -E 'foo|bar|baz' file.txt
-F, --fixed-strings PATTERNS are strings
要點:把模式當作純文本匹配,不解析正則,速度最快,適合關鍵詞列表。
示例:
grep -F 'a.b' file.txt # 匹配字面 "a.b"
-G, --basic-regexp PATTERNS are basic regular expressions
要點:使用基本正則(BRE,很多系統默認)。在 BRE 中某些元字符(如 + ? | ()
)需要轉義。
示例:
grep -G 'foo\|bar' file.txt # 在 BRE 中 "或" 用 \|
-P, --perl-regexp PATTERNS are Perl regular expressions
要點:啟用 PCRE(Perl 風格正則),支持斷言、\b、懶惰量詞等高級特性。注意并非所有系統都啟用了 -P
。
示例:
grep -Po '(?<=ID=)\d+' data.txt # 提取 ID= 后的數字
-e, --regexp=PATTERNS use PATTERNS for matching
要點:顯式指定一個模式;可重復使用 -e
指定多個模式;適合模式以 -
開頭時避免被誤解析為選項。
示例:
grep -e '^Error' -e '^Warning' logfile
-f, --file=FILE take PATTERNS from FILE
要點:從文件讀取多個模式(文件每行一個模式),和 -e
等效但便于管理大量模式。
示例:
grep -Ff keywords.txt big.log
-i, --ignore-case ignore case distinctions in patterns and data
要點:忽略大小寫匹配(模式與數據均不區分大小寫)。
示例:
grep -i 'linux' README
--no-ignore-case do not ignore case distinctions (default)
要點:恢復區分大小寫(這是默認行為),通常無需顯式指定。
示例:
grep --no-ignore-case 'Makefile' files.list
-w, --word-regexp match only whole words
要點:匹配完整單詞,單詞邊界按“字母/數字/下劃線”定義。連字符或點不被視為詞字符。
示例:
grep -w 'is' file.txt # 不會匹配 "this"
-x, --line-regexp match only whole lines
要點:行內容必須完全等于模式才匹配(整行匹配)。
示例:
grep -x 'enabled=true' config.txt
-z, --null-data a data line ends in 0 byte, not newline
要點:把 NUL(\0)當作行分隔符而非換行,用于處理包含換行的記錄或與 find -print0
、xargs -0
聯動時更安全。
示例:
# 與 -Z/-0 搭配用于 NUL 安全的文件名流
find . -type f -print0 | xargs -0 grep -z 'pattern'
雜項
-s, --no-messages suppress error messages
要點:抑制錯誤輸出(如權限錯誤或不存在的文件),腳本中用于避免噪音。
示例:
grep -s 'secret' /root/*
-v, --invert-match select non-matching lines
要點:反選,輸出不匹配模式的行,用于過濾包含某關鍵詞的行之外的內容。
示例:
grep -v 'DEBUG' app.log
-V, --version display version information and exit
要點:顯示 grep
的版本然后退出。
示例:
grep -V
--help display this help text and exit
要點:顯示幫助文本并退出。
示例:
grep --help
輸出控制
-m, --max-count=NUM stop after NUM selected lines
要點:在每個輸入文件上匹配到 NUM
行后停止處理該文件(提高速度、用于“是否存在”檢查)。
示例:
grep -m1 'fatal' big.log
-b, --byte-offset print the byte offset with output lines
要點:輸出每個匹配行在文件中的字節偏移(從 0 開始),便于定位二進制或精確定位。
示例:
grep -bn 'pattern' file.txt
-n, --line-number print line number with output lines
要點:輸出匹配行的行號(文件名:行號:內容)。
示例:
grep -n 'TODO' src/*.c
--line-buffered flush output on every line
要點:開啟逐行刷新,適用于實時管道(但會降低性能)。
示例:
tail -f app.log | grep --line-buffered 'ERROR'
-H, --with-filename print file name with output lines
要點:在輸出前加上文件名;在多文件搜索時默認啟用,但可強制顯示。
示例:
grep -H 'alloc' src/*.c
-h, --no-filename suppress the file name prefix on output
要點:不顯示文件名(適合單文件或只想要匹配行本身)。
示例:
grep -h 'pattern' file1 file2
--label=LABEL use LABEL as the standard input file name prefix
要點:當從標準輸入讀取時,用給定標簽代替文件名顯示,便于記錄來源。
示例:
cat data | grep --label=STDIN -H 'pattern'
-o, --only-matching show only nonempty parts of lines that match
要點:只輸出匹配到的子串,不輸出整行。常用于抽取 URL、數字、鍵值等。
示例:
grep -Eo 'https?://[^ ]+' page.html
-q, --quiet, --silent suppress all normal output
要點:靜默模式,不打印任何匹配內容,用退出碼判斷是否存在匹配(適合腳本判斷)。
示例:
if grep -q 'READY' status.txt; then echo OK; fi
--binary-files=TYPE assume that binary files are TYPE; TYPE is 'binary', 'text', or 'without-match'
要點:設置遇到二進制文件時的處理策略:
binary
:按二進制對待(默認;匹配時通常顯示 “Binary file matches”);text
:把二進制當作文本處理;without-match
:把二進制視為沒有匹配(忽略)。
示例:
grep --binary-files=text 'PNG' image.bin
grep --binary-files=without-match 'pattern' *
-a, --text equivalent to --binary-files=text
要點:等價于把二進制文件當作文本處理。
示例:
grep -a 'TODO' file.bin
-I equivalent to --binary-files=without-match
要點:等價于把二進制文件視為不匹配(忽略)。
示例:
grep -I 'pattern' *
-d, --directories=ACTION how to handle directories; ACTION is 'read', 'recurse', or 'skip'
要點:指定當遇到目錄時的處理方式:
read
:把目錄當文件讀(很少用);recurse
:進入目錄遞歸搜索;skip
:跳過目錄。
示例:
grep -d skip 'needle' .
-D, --devices=ACTION how to handle devices, FIFOs and sockets; ACTION is 'read' or 'skip'
要點:控制如何處理設備、FIFO、套接字,避免阻塞或不必要讀取。
示例:
grep -D skip -R 'pattern' /
-r, --recursive like --directories=recurse
要點:遞歸搜索目錄(等同 --directories=recurse
)。默認不跟隨符號鏈接。
示例:
grep -rn 'TODO' src/
-R, --dereference-recursive likewise, but follow all symlinks
要點:遞歸并跟隨所有符號鏈接(注意可能形成循環,需要配合 --exclude-dir
限制)。
示例:
grep -Rni --exclude-dir=.git 'init' .
--include=GLOB search only files that match GLOB (a file pattern)
要點:只在匹配指定文件名模式的文件中搜索,有助于提速并限制范圍。支持通配如 *.c
或 {*.c,*.h}
。
示例:
grep -Rni --include='*.{c,h,cpp}' 'allocator' .
--exclude=GLOB skip files that match GLOB
要點:排除符合該模式的文件(如 .min.js
、二進制等)。
示例:
grep -Rni --exclude='*.min.js' 'fetch' web/
--exclude-from=FILE skip files that match any file pattern from FILE
要點:從文件讀取排除模式列表(每行一個 GLOB),便于管理復雜排除規則。
示例:
grep -Rni --exclude-from=.greprules 'needle' .
--exclude-dir=GLOB skip directories that match GLOB
要點:排除指定目錄(常用 .git
、node_modules
、build
等)。
示例:
grep -Rni --exclude-dir={.git,node_modules,dist} 'config' .
-L, --files-without-match print only names of FILEs with no selected lines
要點:只列出沒有匹配的文件名,常用于查找缺失項(如缺少版權頭的文件)。
示例:
grep -RL 'Copyright' src/
-l, --files-with-matches print only names of FILEs with selected lines
要點:只列出有匹配的文件名(方便后續批處理)。
示例:
grep -Rl 'panic' /var/log
-c, --count print only a count of selected lines per FILE
要點:對每個文件輸出匹配行數,不顯示具體行。
示例:
grep -Rc 'ERROR' logs/
-T, --initial-tab make tabs line up (if needed)
要點:輸出格式對齊(在帶文件名前綴時用 Tab 對齊)。
示例:
grep -TnH 'pattern' *.txt
-Z, --null print 0 byte after FILE name
要點:在文件名后輸出 NUL(\0),便于以 NUL 為分隔的管道安全處理,例如與 xargs -0
聯動。
示例:
grep -Zl 'needle' -R . | xargs -0 -I{} echo "FOUND: {}"
上下文控制
-B, --before-context=NUM print NUM lines of leading context
要點:輸出匹配行以及該行之前的 NUM
行,便于查看上下文。
示例:
grep -B 2 'OutOfMemoryError' app.log
-A, --after-context=NUM print NUM lines of trailing context
要點:輸出匹配行以及之后的 NUM
行。
示例:
grep -A 2 'OutOfMemoryError' app.log
-C, --context=NUM print NUM lines of output context
要點:-C NUM
等價于同時指定 -B NUM
和 -A NUM
,顯示匹配行前后 NUM
行。
示例:
grep -C 3 'OutOfMemoryError' app.log
-NUM same as --context=NUM
要點:簡寫形式,例如 -3
相當于 -C 3
。
示例:
grep -R3 'failed' logs/
--group-separator=SEP print SEP on line between matches with context
要點:當多組匹配(有上下文)時,在組間插入自定義分隔符 SEP
。默認分隔是 --
。
示例:
grep -C1 --group-separator='-----' 'error' app.log
--no-group-separator do not print separator for matches with context
要點:取消組間分隔(把多個匹配塊連續顯示)。
示例:
grep -C2 --no-group-separator 'warn' app.log
--color[=WHEN], --colour[=WHEN] use markers to highlight the matching strings; WHEN is 'always', 'never', or 'auto'
要點:為匹配部分加顏色標記。WHEN
參數控制啟用時機:always
總是啟用,never
禁用,auto
在交互式終端時啟用。
示例:
grep --color=auto -n 'main' *.c
-U, --binary do not strip CR characters at EOL (MSDOS/Windows)
要點:不要去掉行尾 \r
(在處理 CRLF 文件時保留回車字符),便于診斷 Windows 格式換行問題。
示例:
grep -U '$' windows.txt | cat -A # 顯示 ^M 等
輸入 / 默認行為 / 退出狀態
When FILE is '-', read standard input.
要點:將文件名指定為 -
表示從標準輸入讀取數據。
示例:
echo "hello" | grep 'h' - # "-" 表示 stdin
With no FILE, read '.' if recursive, '-' otherwise.
要點:未指定文件名時,如果使用遞歸選項(如 -r
),grep
默認對當前目錄 .
進行遞歸搜索;否則默認讀取標準輸入。
示例:
grep -R 'pat' # 等同 grep -R 'pat' .
echo "abc" | grep 'a' # 無文件且不遞歸 → 讀 stdin
With fewer than two FILEs, assume -h.
要點:當輸入的文件少于兩個時(例如單個文件或標準輸入),默認不打印文件名前綴,相當于 -h
。
示例:
grep 'pattern' single_file.txt # 不顯示文件名前綴
Exit status is 0 if any line is selected, 1 otherwise; if any error occurs and -q is not given, the exit status is 2.
要點:退出碼含義:
0
:至少有一行匹配;1
:沒有匹配;2
:發生錯誤(例如無法讀取文件),除非使用-q
(靜默)。
腳本中常用grep
的退出碼做條件判斷。
示例:
if grep -q 'READY' status.txt; then echo OK; else echo NOT_READY; fi
附 — 常見組合示例
- 在代碼目錄中遞歸查找包含
alloc
的.c/.h
文件,排除.git
與build
:
grep -Rni --include='*.{c,h,cpp}' --exclude-dir={.git,build} 'alloc' .
- 統計項目中每個文件的
ERROR
出現次數:
grep -Rc 'ERROR' logs/
- 從標準輸入抽取所有 URL:
curl -s http://example.com | grep -Eo 'https?://[^ ]+'
- 找出沒有版權聲明的源文件:
grep -RL 'Copyright' src/
- 用 NUL 安全方式查找并處理文件名(與 xargs -0 配合):
grep -Zl 'needle' -R . | xargs -0 -I{} echo "FOUND: {}"
- 只查找以數字 ID 出現的位置(PCRE 斷言):
grep -Po '(?<=ID=)\d+' data.txt
用法示例
看完能獨立寫出常用 90% 場景的 grep 命令。
A. 基礎檢索
# 在兩個文件里找 hello world(忽略大小寫)
grep -i 'hello world' menu.h main.c
-i
:大小寫不敏感。找到了就打印整行。
# 在當前目錄遞歸找包含 "TODO" 的行,并顯示 文件名:行號:內容
grep -Rni --include='*.{c,h,cpp}' 'TODO' .
-R
:遞歸且跟隨鏈接;-n
:行號;-H
默認在多文件時會加文件名;--include
縮小范圍以提速。
B. 選擇合適的模式類型
# ERE:函數名(帶括號),更符合直覺
grep -REn 'my_func\(' src/
- 在 ERE 下
(
不是特殊字符,但\(
可讀性更好,避免歧義;若在 BRE(默認-G
)里則 必須 寫\(
。
# 純文本多關鍵詞(最快):把關鍵詞寫到文件里,每行一個
grep -Ff keywords.txt -R --include='*.log' -n .
# PCRE:用詞邊界和后行斷言抽取 ID
grep -aPo '(?<=ID=)\d+' big.bin
-a
把潛在二進制當文本;-P
支持斷言;-o
只輸出匹配片段(純數字 ID)。
C. 輸出控制(文件名、計數、僅匹配)
# 只列出含有匹配的文件名
grep -Rl 'panic' /var/log
# 只列出沒有匹配的文件名(比如找“缺少版權頭”的源文件)
grep -RL 'Copyright .* MyCorp' src/
# 只統計每個文件匹配行數
grep -Rc 'ERROR' logs/
# 只輸出匹配到的關鍵詞,便于統計頻次
grep -Rho --include='*.py' -E 'TODO|FIXME' . | sort | uniq -c | sort -nr
D. 反選與上下文
# 反選:輸出不含 DEBUG 的行
grep -Rnv --include='*.log' 'DEBUG' logs/# 上下文:匹配行前后各 3 行
grep -RniC3 'OutOfMemoryError' logs/
# 或寫成:grep -Rni -A3 -B3 'OutOfMemoryError' logs/
E. 性能與停止條件
# 每個文件匹配到第一行就停(定位“是否存在”)
grep -m1 -R 'needle' .
# 大量固定字符串,極致提速
LC_ALL=C grep -F --include='*.txt' -R -n -e 'foo' -e 'bar' -e 'baz' .
F. 二進制與 NUL 安全流水線
# 把二進制當文本搜
grep -aR 'PNG' .
# 和 find/xargs 進行 NUL 安全聯動,只輸出匹配的文件名(以 NUL 結尾)
find . -type f -name '*.txt' -print0 \
| xargs -0 grep -Zl 'needle' \
| tr '\0' '\n' # 僅為了人眼閱讀
G. 目錄/設備處理與排除
# 跳過特定目錄,避免巨慢
grep -Rni --exclude-dir={.git,node_modules,dist,build} 'register' .# 處理目錄/設備的策略
grep -D skip -d recurse -R 'pattern' .
-d/--directories
:遇到目錄 read/recurse/skip-D/--devices
:遇到設備/FIFO/socket read/skip
H. 腳本里更健壯
# 靜默測試(只看退出碼)
if grep -qE '^(enabled|on|true)$' config.txt; thenecho "feature enabled"
fi
-q
靜默;-E
用擴展正則;配合退出碼0/1/2
做邏輯分支。