對應 30天挑戰精通 PowerShell 該書第 21 章。
正規表示式( regular expression )有時也稱為 regex,在文字解析( text parsing )方面非常實用,這在 UNIX 和 Linux 作業系統裡是常有的需求。而在 PowerShell 中,你進行文字解析的頻率通常較低,需要用到 regex 的情況也相對較少。話雖如此,我們清楚地知道,在 PowerShell 中,有時你需要解析像日誌檔案( log file )這樣的文字內容。這正是本章介紹正規表示式的方法:作為解析文字檔的工具
Reference - P.331
最早接觸正規表示式是透過 Python 學習的,這次本來也打算在 Regex101 上進行一些線上測試。結果沒想到,頁面上竟然沒有 PowerShell 的選項!後來查了一下,發現其實可以使用 .NET 7.0 (C#) 來操作,因為 PowerShell 和 C# 都是基於 .NET framework 的正規表示式引擎,不過可能在這幾個區塊需要特別注意:
正規表示式 (regex) 的主要功能是用來在文字中找到符合特定規則或模式的資料。打個比方,在一個
Case 裡,有一個日誌檔案,我們需要從中找出所有發生錯誤的記錄,或者過濾出某些特定的時間段裡的資訊時,就可以使用正規表示式來過濾。
這讓我想到一個類似的比喻,你可以想像當你在圖書館找書時,正規表示式就像是你在搜尋分類號、書名或作者的一套非常靈活的規則,它幫助你篩選並快速找到你想要的書。
而在 PowerShell 中,正規表示式可以用來掃描文字檔案中的內容,找出特定的文字組合。比如,你可以找出符合日期格式的所有行,或找出包含特定關鍵字的行。這在處理大型日誌檔案時尤其實用。
正規表示式有一些特殊的字元,它們用來表示不同的匹配規則。這些特殊字元讓你能夠靈活地尋找文字模式,以下是一些常見的正規表示式語法:
字元 | 說明 | 範例 |
---|---|---|
. |
任意一個字元 | a.b 可匹配 "acb", "a2b" |
\* |
重複前面的字元零次或多次 | ab\* 可匹配 "a", "ab", "abbb" |
\d |
任何一個數字 | \d{3} 可匹配三個連續數字 |
\D |
任何一個非數字字符 | \D 可匹配 "a", "!", 但不匹配 "3" |
\s |
空白字符 | a\s+b 可匹配 "a b", "a b" |
\w |
任何一個字母、數字或底線 | \w+ 可匹配 "hello", "a1_23" |
\W |
任何一個非字母、數字或底線 | \W 可匹配 "!", "@", "#", 但不匹配 "a" 或 "3" |
[abc] |
匹配括號內的任一字元 | [aeiou] 可匹配任一母音字母 |
[a-z] |
匹配小寫字母 a 到 z 中任一字元 | [a-z] 可匹配任一小寫字母 |
[^abcde] |
匹配不在括號內的字元 | [^aeiou] 可匹配任一非母音字母 |
^ |
行首匹配 | ^Error 可匹配 "Error" 開頭的行 |
$ |
行尾匹配 | done$ 可匹配以 "done" 結尾的行 |
? |
前面的字元可出現零次或一次 | colou?r 可匹配 "color" 或 "colour" |
{2} |
前面的字元出現兩次 | \d{2} 可匹配兩位數字,如 "42" |
\ |
跳脫特殊字元,使其作為普通字元 | \. 可匹配一個句點 ".",而不是任意字元 |
透過這些符號,你可以組合出極其複雜的模式,用於尋找各種格式的資料。例如,要從文字檔中找出所有電話號碼,你可以用正規表示式 \d{3}-\d{3}-\d{4}
,這樣的語法會匹配類似 "123-456-7890" 的格式。
Reference - about_Regular_Expressions
-Match
是 PowerShell 中用於匹配正規表示式的比較運算子。這個運算子能讓你快速地檢查某個字串是否符合特定的正規表示式規則,並返回布林值 ($true
或 $false
)。
"car" -match "c[aeiou]r"
"c[aeiou]r"
表示要匹配一個字串,這個字串的結構是:
c
開頭。a, e, i, o, u
)。r
結尾。"car"
完全符合這個模式: c
+ a
+ r
,所以結果是 True
。"caaar" -match "c[aeiou]r"
"c[aeiou]r"
只會匹配由 c
開頭、一個母音、再加上 r
的結構。"caaar"
有三個 a
字母,但正規表示式只期望中間有一個母音字母。因此,這個字串不符合條件,結果是 False
。"caaar" -match "c[aeiou]+r"
"c[aeiou]+r"
表示要匹配:
c
開頭。[aeiou]+
中的 +
表示前面的字符可以重複一次或多次)。r
結尾。"caaar"
的結構是 c
+ aaa
+ r
,中間有三個 a
,符合一個或多個母音的條件,因此結果是 True
。"cjinr" -match "c[aeiou]+r"
"c[aeiou]+r"
要求在 c
和 r
之間必須有一個或多個母音( a, e, i, o, u
)。"cjinr"
中間的部分是 jin
,這部分不包含符合條件的母音序列。因此,這個字串不符合正規表示式,結果是 False。"cear" -match "c[aeiou]r"
"c[aeiou]r"
要求:
c
開頭。a, e, i, o, u
)。r
。"cear"
中間的部分是 ea
,這不符合只要一個母音的條件。因此,這個字串不符合模式,結果是 False
。Info: System started successfully.
Error: Unable to connect to server.
Warning: Low disk space.
Error: 404 - Not Found.
Error: 500 - Internal Server Error.
Info: User login successful.
# 設定日誌檔案的路徑
$logFilePath = "/Users/kanglin/code/30days/chapter21_exmaple_log.txt"
# 確認日誌檔案是否存在
if (-not (Test-Path -Path $logFilePath)) {
Write-Output "日誌檔案不存在:$logFilePath"
exit
}
# 讀取日誌檔案內容
$logContent = Get-Content -Path $logFilePath
# 遍歷日誌內容並找出包含 "Error" 的行
foreach ($line in $logContent) {
# 使用 -match 匹配包含 "Error" 的行
if ($line -match "Error") {
# 進一步檢查錯誤代碼的格式,例如 "Error: 404"
if ($line -match "Error:\s*\d{3}") {
# 輸出符合錯誤代碼格式的行
Write-Output "找到錯誤行: $line"
} else {
# 只包含 "Error" 的行
Write-Output "找到一般錯誤行: $line"
}
}
}
Select-String
是 PowerShell 中專門用於搜尋檔案或輸入中的文字的 cmdlet,功能類似於 UNIX/Linux 系統中的 grep
。它允許你快速地在檔案中找到匹配的字串,非常適合與正規表示式一起使用。
假設你有一個日誌檔案,你想要找出所有包含日期格式 "2024-10-08" 的行:
Select-String -Path "C:\logs\mylog.txt" -Pattern "\d{4}-\d{2}-\d{2}"
在這裡,-Pattern
參數用來指定正規表示式模式,Select-String
會在檔案中找出所有符合該模式的行並將其顯示出來。這種方法能夠大大減少你手動尋找的時間,尤其是當檔案非常大時。
你也可以把結果儲存到變數中,並進一步分析:
$errors = Select-String -Path "C:\logs\mylog.txt" -Pattern "Error"
foreach ($error in $errors) {
Write-Output $error.Line
}
Get-Childitem -filter *.log -recurse |
select-string -pattern "\s40[0-9]\s" |
format-table Filename, lineNumber, Line -wrap
在執行前,先透過下列 script 建立環境
New-Item -ItemType Directory -Path .\LogFiles -Force
Set-Content -Path .\LogFiles\app1.log -Value @"
INFO 2024-10-09 This is a test log entry.
WARN 2024-10-09 This line contains a status code 400.
ERROR 2024-10-09 Error code 401 occurred.
INFO 2024-10-09 Normal operation with status 200.
"@
Set-Content -Path .\LogFiles\app2.log -Value @"
ERROR 2024-10-09 Something bad happened, error code 403.
INFO 2024-10-09 All systems operational.
WARN 2024-10-09 Warning! Status code is 404.
ERROR 2024-10-09 Critical failure at code 405.
"@
Set-Content -Path .\LogFiles\app3.log -Value @"
INFO 2024-10-09 This is a general info log.
ERROR 2024-10-09 Error found with status 500.
WARN 2024-10-09 Status 408 reached a warning level.
INFO 2024-10-09 No issues detected.
"@
回頭我們再來逐行看其意義,並執行看看結果:
Get-Childitem -filter *.log -recurse |
select-string -pattern "\s40[0-9]\s" |
format-table Filename, lineNumber, Line -wrap
第一行
Get-ChildItem
:用於列出檔案和目錄。-filter \*.log
:過濾出 .log
結尾的檔案,這意味著只會選取 log 檔。-recurse
:遞迴搜索子目錄,找到所有目錄下的 .log
檔案。第二行
Select-String
:在檔案中搜尋匹配指定模式的行,類似於 Linux 中的 grep
。-pattern "\s40[0-9]\s"
:這裡的正規表示式解釋如下:
\s
:代表空白字符(例如空格或 Tab)。40[0-9]
:匹配 400 到 409 之間的任意數字。\s
:代表結尾的空白字符。因此這個模式會匹配以空白字符開頭,接著是 "40x"(例如 400、401 等),然後結束於另一個空白字符的部分。第三行
Format-Table
:將輸出格式化為表格的形式。Filename
、 LineNumber
、 Line
:顯示匹配行的檔案名、行號和內容。-wrap
:將過長的內容換行顯示。Day 26 - Azure DevOps 概覽