正規表示式在字串處理和分析中扮演著至關重要的角色,尤其在處理大量資料和日誌時,能大幅提升效率。本文除了基本語法外,更著重於實務應用技巧,例如何結合分組提取特定資訊、使用替代簡化多條件比對,以及在 Bash、Nmap 和 NetExec 等工具中的整合應用。此外,文章也探討瞭如何使用正規表示式比對 IP 地址和埠,並結合 grep、sed 和 awk 等指令進行更進階的文書處理,例如從 Nmap 掃描結果中快速篩選出特定主機和埠資訊,或是在 NetExec 輸出中提取使用者名稱和電子郵件。這些技巧能協助開發者和系統管理員更有效率地處理日常工作,並提升程式碼的可讀性和可維護性。文章也強調了從簡單模式開始逐步建構複雜正規表示式的重要性,並建議使用線上測試工具輔助開發和除錯,同時也鼓勵開發者建立個人筆記和參考檔案,以便日後快速查詢和應用。

正規表示式的進階應用與技巧

正規表示式(Regular Expressions,簡稱 regex)是一種強大的工具,用於字串比對和操作。在實務應用中,能夠有效利用正規表示式可以大幅提升程式碼的效率和可讀性。這篇文章將探討一些進階的正規表示式技巧,並透過具體案例來說明其應用。

使用正規表示式比對與提取使用者名稱與電子郵件

我們可以使用以下正規表示式來比對並提取使用者名稱和電子郵件:

([a-zA-Z0-9_]+): ([a-zA-Z0-9_.]+@[a-zA-Z0-9_.]+)

在這個表示式中,[a-zA-Z0-9_]+ 比對一個或多個字母、數字或下劃線(即使用者名稱),而 [a-zA-Z0-9_.]+@[a-zA-Z0-9_.]+ 則比對電子郵件地址。透過分組,我們可以分別提取使用者名稱和電子郵件。

內容解密:

在這個正規表示式中,我們使用了分組來捕捉特定部分的字串。具體來說:

  1. ([a-zA-Z0-9_]+):這部分表示捕捉一個或多個字母、數字或下劃線,這通常對應於使用者名稱。
  2. ::這是一個字面上的冒號,用於分隔使用者名稱和電子郵件。
  3. ([a-zA-Z0-9_.]+@[a-zA-Z0-9_.]+):這部分表示捕捉一個電子郵件地址。它由兩部分組成:
    • [a-zA-Z0-9_.]+:比對一個或多個字母、數字、點號或下劃線,這通常對應於電子郵件的本地部分。
    • @:這是一個字面上的@符號。
    • [a-zA-Z0-9_.]+:比對一個或多個字母、數字、點號或下劃線,這通常對應於電子郵件的網域名稱部分。

這樣設計的正規表示式可以有效地從文字中提取出使用者名稱和電子郵件地址。

實務案例:在 Bash 中使用正規表示式分組

假設我們有一段文字「I love apples and I love oranges」,想要找到所有「I love」的出現位置。在正規表示式中,我們可以寫成 (I love)。這告訴 Bash 將「I love」視為一個單元。

#!/usr/bin/env bash

text="I love apples and I love oranges"
if [[ $text =~ (I love) ]]; then
  echo "Found 'I love'"
fi

在這段程式碼中,(I love) 是正規表示式分組,告訴 Bash 將「I love」視為一個單元。

內容解密:

  1. #!/usr/bin/env bash:這是 Bash 指令碼的 shear bang 行,告訴系統使用 /usr/bin/env 來找到 Bash 的位置。
  2. text="I love apples and I love oranges":定義了一個包含目標文字的變數。
  3. if [[ $text =~ (I love) ]]:使用正規表示式來比對 $text 中的「I love」。=~ 是 Bash 的正則比對運算元。
  4. echo "Found 'I love'":如果比對成功,就輸出「Found ‘I love’」。

利用替代(Alternations)增強捕捉群組

替代(Alternations)在正規表示式中由管道符號 | 表示,類別似於邏輯 OR。它允許我們在同一個正規表示式中指定多個模式,從而提供更靈活的比對方式。

假設我們有一個指令碼需要處理特定副檔名的檔案,例如 .txt.log 檔案。我們可以使用替代來簡化處理邏輯:

#!/usr/bin/env bash

filename="example.txt"
if [[ $filename =~ \.(txt|log)$ ]]; then
  echo "File is either a .txt or .log file."
else
  echo "File is not a .txt or .log file."
fi

內容解密:

  1. #!/usr/bin/env bash:與前面相同,指定指令碼使用 Bash。
  2. filename="example.txt":定義了一個包含檔案名稱的變數。
  3. if [[ $filename =~ \.(txt|log)$ ]]:使用替代來比對 .txt.log 檔案。\. 用於轉義點號(.),因為在正規表示式中點號有特殊含義;而 | 用於替代;最後的 $ 確保模式出現在字串的結尾。
  4. echo "File is either a .txt or .log file."echo "File is not a .txt or .log file.":根據比對結果輸出相應的訊息。

提高效率與可維護性

利用替代可以將多個條件合併成單行程式碼,使指令碼更簡潔且易於維護。特別是在需要處理長列可能性時,替代可以顯著降低指令碼的複雜度。

提示:

  1. 具體化模式:避免模糊的模式導致意外行為。例如,如果只想比對 .txt.log 檔案,就不要寫成 \.(txt|log|doc)$
  2. 測試模式:總是使用各種輸入測試你的模式,以確保其行為如預期。工具如 grep 和線上正則測試器(例如 regex101.com)非常有用。

執行案例

接下來我們將展示如何在實際指令碼中應用替代技巧:

#!/usr/bin/env bash

user_list="john_doe: john.doe@example.com\njane_smith: jane.smith@example.com"
pattern='^([a-zA-Z0-9_]+): ([a-zA-Z0-9_.]+@[a-zA-Z0-9_.]+)$'

while IFS= read -r line; do
  if [[ $line =~ $pattern ]]; then
    echo "Username: ${BASH_REMATCH[1]}, Email: ${BASH_REMATCH[2]}"
  fi
done <<< "$user_list"

內容解密:

  1. 變數宣告:首先宣告包含使用者資料的 user_list 變數和正則模式 pattern
  2. while loop:開始 while loop 迭代讀取 $user_list
  3. match operator:使用 =~ 比較每行 $line 是否符合 $pattern
  4. BASH_REMATCH array:如果符合規範,BASH 自動填充 BASH_REMATCH array 提取各分組資料
  5. 輸出結果:輸出各分組所代表資料

比對 IP 地址與埠

最後我們以一個實際應用展示如何在 Nmap 掃描結果中比對 IP 地址與埠:

grep /open/tcp//http test_nmap.gnmap | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b"

內容解密:

  1. grep /open/tcp//http test_nmap.gnmap:搜尋 Nmap 結果檔案中的特定字串 /open/tcp//http
  2. pipe operator:連結上述命令輸出到下一步命令
  3. grep -oE “\b([0-9]{1,3}.){3}[0-9]{1,3}\b”:從輸出中抽取 IP 地址模型 \b([0-9]{1,3}\.){3}[0-9]{1,3}\b

以上便是一些進階正規表示式技巧及其實務應用範例。希望這些技巧能夠幫助你在日常開發中更高效地處理字串操作。

常見正規表示式實務應用

在測試內部網路的過程中,常常會用到各種正規表示式(regular expressions)來處理和分析資料。這些正規表示式工具不僅簡單易用,而且非常強大,能夠幫助我們快速找到所需的資訊。這裡玄貓將分享一些常用的 grep、sed 和 awk 正規表示式技巧,並附上實際應用案例。

使用 grep 來篩選資料

在測試過程中,玄貓經常會使用 grep 來篩選出特定的資料。例如,在內部網路測試中,玄貓會使用 NetExec 工具來列舉可以使用已取得憑證存取的 SMB 檔案分享。NetExec 是一個強大的工具,可以幫助我們快速找到可存取的分享資源。

NetExec 的使用案例

假設玄貓在一個擁有數百甚至數千台主機的大型網路中進行測試,並且已經使用 NetExec 進行了 SMB 檔案分享列舉掃描。掃描結果儲存在 nxc.log 檔案中。現在,玄貓希望找到那些可以讀取或寫入的分享資源,但不想看到 IPC$ 或 PRINT$ 這類別分享。

cat nxc.log | grep -e READ -e WRITE | grep -v "IPC\$" | grep -v "PRINT\$"

此圖示

  graph TD;
    A[cat nxc.log] --> B[grep -e READ -e WRITE];
    B --> C[grep -v "IPC\$"];
    C --> D[grep -v "PRINT\$"];
內容解密:
  • cat nxc.log:將 nxc.log 檔案的內容輸出到標準輸出。
  • grep -e READ -e WRITE:使用 grep 命令過濾出包含 READWRITE 的行。
  • grep -v "IPC\$":反向過濾出不包含 IPC$ 的行。
  • grep -v "PRINT\$":反向過濾出不包含 PRINT$ 的行。

使用 sed 來編輯文字

sed(stream editor)是一個強大的文字編輯工具,可以用來進行文字的替換和編輯操作。例如,當我們想要隱藏 Nmap 檔案中的 IP 地址時,可以使用 sed 命令來替換所有的 IP 地址。

sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/REDACTED_IP/g' test_nmap.gnmap

此圖示

  graph TD;
    A[sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/REDACTED_IP/g'] --> B[test_nmap.gnmap];
    B --> C["REDACTED_IP"];
內容解密:
  • -E:啟用擴充套件正規表示式。
  • 's/([0-9]{1,3}\.){3}[0-9]{1,3}/REDACTED_IP/g':將所有符合 IP 地址模式的字串替換為 REDACTED_IP
    • s/.../.../g:表示替換操作,其中 ... 是要替換的模式,後面的 g 表示全域性替換。
    • ([0-9]{1,3}\.){3}[0-9]{1,3}:這是比對 IP 地址的正規表示式模式。

如果我們想要將編輯後的結果儲存到新檔案中,可以使用以下命令:

sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/REDACTED_IP/g' test_nmap.gnmap > new_test_nmap.gnmap

使用 awk 處理表格資料

awk 是一個功能強大的文書處理語言,特別適合處理結構化的表格資料。它可以根據特定模式和動作來處理每一行記錄。以下是一些基本的 awk 用法。

awk 基本概念

在開始之前,我們需要了解一些基本概念:

  • Record:每一行記錄。
  • Field:每一列欄位。
  • $n:表示第 n 個欄位,$0 表示整行記錄,$1 表示第一個欄位,$2 表示第二個欄位等。
  • $NF:表示最後一個欄位。
  • $NR:表示當前記錄號。
  • -F:指定欄位分隔符號。

假設我們有一個系統程式列表(來自 ps -ef 命令),我們可以使用 awk 來處理這些資料。

ps -ef | awk '{print $0}'

高階 awk 用法

假設我們想要列印所有屬於特定使用者(例如 author)的程式命令(CMD),可以使用以下命令:

ps -ef | awk '$1 == "author" {print $8}'

應用正規表示式和自定義分隔符號

如果我們想要使用正規表示式比對某些行,並且以自定義分隔符號輸出結果,可以這樣做:

ps -ef | awk '/some_pattern/ {print $1 ":" $2}'

使用正規表示式進行文書處理的技巧與最佳實踐

在各種程式語言中,正規表示式(regex)是一個強大的工具,用於比對和操作字串。無論是簡單的文字搜尋和替換,還是複雜的資料解析和驗證,regex 都能提供高效的解決方案。然而,掌握正規表示式並不是一件容易的事情,特別是在面對複雜模式時。以下是一些實用的技巧和最佳實踐,幫助你更好地建立和使用正規表示式。

從簡單開始

當你開始編寫正規表示式時,從簡單的模式開始是非常有幫助的。逐步引入更多的複雜性,這樣可以讓你更容易理解每個部分的作用,並且更容易除錯。

例如,假設你想比對一個電子郵件地址,你可以從簡單的模式開始:

\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b

使用線上測試工具

有許多線上工具可以幫助你測試和除錯正規表示式,如 Regex101 和 Regexr。這些工具不僅能幫助你測試不同的模式和標誌,還能提供詳細的解釋和建議。

逐步分解

當你面對一個複雜的正規表示式時,將其分解成較小的部分會讓你更容易理解每個組成部分的作用。例如,如果你有一個比對日期格式(YYYY-MM-DD)的正規表示式:

\b(?:19|20)\d\d-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])\b

你可以將其分解成三個部分:年、月和日。

參考檔案與筆記

保持一份正規表示式速查表或參考在手邊,直到你對常見模式和特殊字元感到更自信為止。你也可以自己製作一份筆記,記錄下你學習過程中的難點。這樣可以幫助你更好地記住這些概念。

內容解密:

逐步分解

\b(?:19|20)\d\d    # 比對年份部分
-(?:0[1-9]|1[0-2]) # 比對月份部分
-(?:0[1-9]|[12][0-9]|3[01])\b # 比對日期部分

使用 Awk 和正規表示式進行文書處理

Awk 是一個強大的文書處理工具,它可以結合正規表示式來進行複雜的文字操作。以下是一個使用 Awk 和正規表示式來處理文字並輸出自定義結果的例子:

假設你有一個檔案 log.txt,內容如下:

irq/42-pciehp some data here
irq/56-pciehp some other data here
other irrelevant data

我們希望比對並提取第八列以 irq/ 開頭且後面跟隨兩位數字再接 -pciehp 的記錄,並將第一列與第八列用 ---> 分隔。

awk '$8 ~ /^irq\/[0-9]{2}-pciehp$/{print $1 " ---> " $8}' log.txt

此圖示展示了 Awk 與正規表示式結合處理文字的基本原理:

  graph TD;
    C[C]
    D[D]
    A[輸入檔案] --> B[AWK 程式];
    B --> C{檢查第八列};
    C -- 是 --> D{提取第一列與第八列};
    D --> E[輸出結果];
    C -- 否 --> F[忽略該行];

內容解密:

AWK 語法與正規表示式

awk '$8 ~ /^irq\/[0-9]{2}-pciehp$/{print $1 " ---> " $8}' log.txt

程式流程

  1. 輸入檔案:讀取 log.txt 中的每一行。
  2. AWK 程式:使用 AWK 命令來處理每行。
  3. 檢查第八列:判斷第八列是否符合指定的正規表示式。
  4. 提取第一列與第八列:如果符合條件,提取第一列與第八列並用 ---> 分隔。
  5. 輸出結果:將結果列印預出來。
  6. 忽略該行:如果不符合條件,忽略該行。