grep 查詢文字檔內容

平常寫程式或做作業常要翻 log 檔案。檔案太大時根本看不完。這段 Python 腳本可以幫上忙。它像是一個客製化的 grep 工具。支援關鍵字高亮顯示。還能決定要看關鍵字前後幾行內容。這對分析程式報錯很有幫助。

如何開始使用

下載程式碼:
https://github.com/max32002/MaxFontScripts/blob/master/grep.py

確保你的電腦已經安裝了 Python 3 環境。你可以透過終端機或命令提示字元來執行它。

基本指令格式

python grep.py 關鍵字 路徑

你可以加上這些參數:

  • -b 數字:顯示關鍵字前面幾行。
  • -a 數字:顯示關鍵字後面幾行。
  • -i:忽略英文字母大小寫。
  • -l:把找到的關鍵字標上顏色。
  • -E:開啟正規表示法搜尋模式。
  • -ext .txt:搜尋指定副檔名。
  • -o 檔名:將搜尋結果存成檔案。
  • -r or -R:預設只會掃描該層資料夾,除非你加上 -r 才會遞迴搜尋子目錄。

實際操作範例

假設要找 logs 資料夾內所有包含 ERROR 的行。並且要看錯誤發生的前 2 行與後 2 行。還要標記顏色。

python grep.py ERROR ./logs -b 2 -a 2 -l

如果要用正規表示法找特定格式。例如找 IP 位址。並限制只找 .txt 檔案。

python grep.py "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" ./data -E -ext .txt

如果想把搜尋結果存起來。方便之後寫報告用。

python grep.py "Login Failed" auth.log -o result.txt

這套工具邏輯很單純。不用安裝複雜的套件。做實驗數據分析或是 debug 都很實用。


正規表示式(Regular Expression)在處理文字時非常強大。以下整理了最常用的規則與符號,不論是在 Python、JavaScript 或是終端機工具都通用。

基本字元與邊界

這些符號用來定位文字的位置或是匹配特定類型的單一字元。

符號說明範例
.匹配除換行符號外的任何單一字元a.c 可匹配 abc, a2c
^匹配字串的開頭^Hello 必須以 Hello 開頭
$匹配字串的結尾done$ 必須以 done 結尾
\d匹配任何數字,等同於 [0-9]\d\d 匹配兩位數
\D匹配任何非數字字元\D 匹配字母或符號
\w匹配字母、數字或底線\w+ 匹配一個單字
\s匹配任何空白字元(空格、Tab、換行)\s+ 匹配連續空白
\b匹配單字邊界\bcat\b 只匹配 cat 不含 category

量詞(次數匹配)

用來決定前面的字元可以連續出現幾次。

  • *:出現 0 次或多次。
  • +:出現 1 次或多次(至少要有一個)。
  • ?:出現 0 次或 1 次(選用性質)。
  • {n}:精確出現 n 次。
  • {n,}:出現至少 n 次。
  • {n,m}:出現 n 到 m 次之間。

字元集合與群組

當你需要指定一段範圍或是將多個規則組合在一起時。

  • [abc]:匹配括號內的任一字元。
  • [^abc]:匹配除了 abc 以外的任何字元。
  • [a-z]:匹配小寫字母 A 到 Z。
  • [A-Z]:匹配大寫字母 A 到 Z。
  • |:邏輯「或」(OR),例如 cat|dog 匹配 cat 或 dog。
  • (…):擷取群組,將規則組合在一起,之後可以用 \1 或在程式中提取。
  • (?:…):非擷取群組,只組合不記錄內容,效能較好。

貪婪與懶惰匹配

這是進階使用時最常遇到的陷阱。

預設情況下,量詞是「貪婪的」,會盡可能匹配最長的字串。例如對於 <div>test</div> 使用 <.*> 會匹配到整個字串。

如果在量詞後面加上問號(例如 *?+?),會變成「懶惰模式」,只匹配到第一個符合條件的結尾。例如 <.*?> 只會匹配到 <div>


實戰小筆記

在撰寫規則時,如果遇到特殊符號如 .*?( 等,必須在前面加上反斜線 \ 進行跳脫,才能匹配符號本身。

例如要匹配一個 IP 地址,簡單的寫法會是:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}

你想看如何將這些規則應用在你之前的 versionName 修改腳本中嗎?


非擷取群組 (?:...) 的主要作用是「只組合、不存檔」。它讓你可以對一組字元進行量詞操作,但不會在記憶體中記錄這個群組的內容,這在處理複雜字串或提升效能時很有用。

範例一:純粹為了使用量詞

如果你想匹配「連續出現多次的單字」,但你只關心整個字串,不需要單獨取出那個單字。

  • 正規表示式^(?:https?|ftp)://[^\s]+$
  • 說明:這裡用 (?:https?|ftp) 把通訊協定組合起來,後面加個 | 代表「或」。如果你用 (https?|ftp),程式會額外幫你存下一個變數(Group 1)記錄到底是匹配到 http 還是 ftp。用非擷取群組則可以省去這個開銷。

範例二:搭配擷取群組使用

當你的正規表示式很長,裡面有很多層括號,使用非擷取群組可以讓你的「編號」更乾淨。

  • 待處理文字ID: 123-ABC, ID: 456-DEF
  • 正規表示式ID: (\d+)-(?:[A-Z]+)
  • 程式結果
    • Group 1 會拿到 123
    • 因為 [A-Z]+ 放在 (?:...) 裡面,它不會被分配到 Group 2
    • 這在寫程式時,可以避免去算到底現在是第幾個括號。

範例三:在分割字串時的差異 (以 Python 為例)

這是在實作中最明顯的區別。

Python

import re

text = "apple123orange456banana"

# 使用一般群組:分隔符號會被保留在結果清單中
print(re.split(r"(\d+)", text)) 
# 輸出: ['apple', '123', 'orange', '456', 'banana']

# 使用非擷取群組:分隔符號會被丟棄,只留下分割後的內容
print(re.split(r"(?:\d+)", text)) 
# 輸出: ['apple', 'orange', 'banana']

總結建議

當你發現括號只是為了「要把這幾個字看成一體」或是「為了用 OR 邏輯」,而不是為了之後要把這段文字抽出來用時,就應該習慣加上 ?:。這不僅能讓程式跑得快一點,也能讓後續維護程式碼的人(或你自己)知道,這一段內容不需要被抓取。