Bash 中的正規表示式匹配 文章首圖

Bash 中的正規表示式匹配

Bash 中的正規表示式匹配

在 Linux 系統管理與自動化腳本開發中,字串處理是日常工作的核心。雖然 grepsed 等外部命令強大且靈活,但它們需要啟動子進程,這在效能敏感或需要即時判斷的場景中並非最佳選擇。Bash 內建的 [[ ... ]] 結構結合 =~ 運算子,提供了一種高效、原生的正規表示式(Regular Expression)匹配機制。這篇文章將深入探討如何在 Bash 腳本中優雅地使用正規表示式,並提供實用的範例。

為什麼選擇 Bash 內建匹配?

在撰寫 Shell 腳本時,若需驗證使用者輸入、解析日誌檔或檢查檔案格式,我們常會遇到如下情境:

if [[ "$filename" =~ ^report_[0-9]{4}-[0-9]{2}-[0-9]{2}\.csv$ ]]; then
    echo "檔案格式正確"
else
    echo "格式錯誤"
fi

與使用 grep -E 相比,內建匹配避免了子進程的開銷,且能直接將匹配結果存入陣列變數,方便後續處理。

基本語法與用法

Bash 的正規表示式匹配語法為:

[[ string =~ regex ]]

值得注意的是,正規表示式不應加引號。若加上引號,Bash 會將其視為普通字串而非正規表示式模式,導致匹配失敗。此外,Bash 使用的是 Extended Regular Expressions (ERE),這與 grep -E 的語法相容。

實戰範例:提取 IP 地址

假設我們需要從一串網路日誌中提取 IPv4 地址。IPv4 地址由四個 0-255 的數字組成,中間以點號分隔。雖然完整的 IPv4 正規表示式較為複雜,但我們可以先使用一個簡化版本來演示分組捕獲的功能。

當使用括號 () 將正規表示式的部分內容包起來時,匹配到的子字串會被存入特殊變數 BASH_REMATCH 陣列中。BASH_REMATCH[0] 是整個匹配的字串,BASH_REMATCH[1] 是第一個括號內的內容,以此類推。

以下是一個完整的腳本範例:

#!/bin/bash

# 定義測試字串
log_line="2023-10-27 14:30:00 ERROR Connection from 192.168.1.105 refused"

# 定義正規表示式
# 我們希望捕捉 IPv4 地址
# [0-9]{1,3} 匹配 1 到 3 位數字
# \. 匹配實際的點號
regex='([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})'

if [[ $log_line =~ $regex ]]; then
    echo "匹配成功!"
    echo "完整匹配: ${BASH_REMATCH[0]}"
    echo "捕獲的 IP: ${BASH_REMATCH[1]}"
else
    echo "未找到 IP 地址"
fi

執行此腳本,輸出將為:

匹配成功!
完整匹配: 192.168.1.105
捕獲的 IP: 192.168.1.105

進階應用:驗證電子郵件格式

正規表示式在資料驗證中極具價值。以下範例展示如何檢查使用者輸入的字串是否符合基本的電子郵件格式。

#!/bin/bash

read -p "請輸入電子郵件地址: " email

# 基本電子郵件驗證正規表示式
# ^[a-zA-Z0-9._%+-]+ 開頭為合法字元
# @ 符號
# [a-zA-Z0-9.-]+ 網域名稱部分
# \.[a-zA-Z]{2,}$ 頂層網域至少兩個字母
email_regex='^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'

if [[ $email =~ $email_regex ]]; then
    echo "✅ 有效的電子郵件地址"
else
    echo "❌ 無效的電子郵件地址"
fi

此腳本在 Ubuntu 22.04 或 Debian 12 上可直接執行。請注意,正規表示式的驗證僅能確保格式大致正確,無法驗證信箱是否真實存在。

常見問題與注意事項

1. 變數與正規表示式的關係

許多新手會犯一個錯誤,將正規表示式直接寫在引號內:

# 錯誤示範
if [[ $string =~ "^pattern$" ]]; then
    echo "Match"
fi

這會導致匹配失敗,因為 Bash 會將 "^pattern$" 視為字面字串。正確做法是將正規表示式存入變數,或直接在 =~ 後方寫入未加引號的模式:

# 正確示範 1:使用變數
pattern="^pattern$"
if [[ $string =~ $pattern ]]; then ...

# 正確示範 2:直接寫入
if [[ $string =~ ^pattern$ ]]; then ...

2. 特殊字元的轉義

在 Bash 中,某些特殊字元如 (){} 在正規表示式中具有特殊意義(用於分組或重複計數)。若你要匹配字面意義的這些字元,必須使用反斜線 \ 進行轉義。例如,匹配括號本身需寫為 \(\)。然而,在 ERE 中,括號用於分組時不需轉義,這與基礎正規表示式(BRE)不同,需特別留意。

小結

Bash 內建的正規表示式匹配功能為系統管理員提供了一個強大且高效的工具。透過熟悉 [[ ... =~ ... ]] 語法與 BASH_REMATCH 陣列,我們可以在不依賴外部命令的情況下,完成複雜的字串驗證與提取任務。掌握這些技巧,將顯著提升 Shell 腳本的效能與可讀性,讓你的自動化工作流更加流暢。