National Chengchi University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

1

Local AI

--

Uedu Code

--

政治大學 AQI 58 25°C PM2.5 12
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 14

正規表達式

grep、搜尋固定字串、集合與行首行尾
Interaction 20 minute(s)

Learning Objectives

  • 用 grep 找出含有關鍵字的行,搭配 -i、-n、-v、-c、-r 選項
  • 用管線把其他指令的輸出交給 grep,再交給 wc 計算符合的行數
  • 用 ^ 與 $ 比對行首與行尾,用 . 與 * 比對任意字元與重複
  • 用 [abc]、[^a-z]、[[:digit:]] 等集合比對一個字元,知道基本正規表達式裡 + ? | 要加反斜線

用樣式描述想找的文字

設定檔與紀錄檔動輒上千行,要找「含有 error 的行」「以 # 開頭的註解」「有數字的行」,一行一行看並不實際。 正規表達式(regular expression,簡稱 regex 或 RE)是一套描述文字樣式的寫法,grep、sed、awk、各種程式語言與編輯器都支援。 它以「行」為單位處理文字,可以搭配特殊符號完成搜尋、刪除、取代。

grep 會逐行檢查輸入,印出符合樣式的整行。名稱來自早期編輯器 ed 的指令 g/re/p(global/regular expression/print)。

注意:正規表達式和上一課的萬用字元長得很像,意思卻不同。

符號 萬用字元(shell 比對檔名) 正規表達式(grep 比對文字內容)
* 任意多個字元 前一個字元重複 0 次以上
? 剛好 1 個字元 前一個字元可有可無(基本正規表達式要寫 \?)
. 一般的點 任意 1 個字元

所以樣式一定要用單引號包起來,避免 shell 把它當成萬用字元先展開。

grep 的常用選項

grep [選項] '樣式' 檔案…
指令 | grep [選項] '樣式'
選項 作用
-i 不分大小寫(ignore case)
-n 在每一行前面加上行號
-v 反向:列出不符合的行(invert)
-c 只顯示符合的行數(count)
-r 遞迴搜尋目錄底下所有檔案(recursive)
-l 只列出含有符合內容的檔名
-o 只印出符合的那一段文字,而不是整行

以 /etc/os-release 為例:

$ grep ubuntu /etc/os-release
ID=ubuntu
HOME_URL="https://www.ubuntu.com/"
$ grep -in ubuntu /etc/os-release
1:PRETTY_NAME="Ubuntu 24.04.1 LTS"
2:NAME="Ubuntu"
6:ID=ubuntu
8:HOME_URL="https://www.ubuntu.com/"
9:UBUNTU_CODENAME=noble
$ grep -c ubuntu /etc/os-release
2

不加 -i 時大小寫視為不同,所以 Ubuntu、UBUNTU 都沒有被找到。

本課的輸出取自練習用的模擬器,模擬器裡的 /etc/os-release 與 /etc/passwd 是精簡過的版本; 真的 Ubuntu 這兩個檔案行數較多,找到的行與計數會比這裡多。

grep 也能放在管線裡,篩選其他指令的輸出;再接 wc -l 就能數出有幾行:

$ cat /etc/passwd | grep root
root:x:0:0:root:/root:/bin/bash
$ grep nologin /etc/passwd | wc -l
4

grep -r 樣式 目錄 會搜尋目錄底下所有檔案,每一行前面加上檔名。一般使用者讀不到的檔案會出現 Permission denied, 和上一課的 find 一樣可以用 2>/dev/null 丟掉。投影片的 grep network /etc/*.conf | wc -l 則是用萬用字元列出多個檔案,再數出符合的行數。

互動練習:grep 的常用選項

對應投影片的 Lab01、Lab02。以 /etc/os-release 與 /etc/passwd 練習 -i、-n、-v、-c,再用管線把 grep 的結果交給 wc -l 計算,最後在整個 /etc 裡遞迴搜尋。按鈕都是真的指令;有 | 的指令執行後,右側會畫出管線每一段的輸出。

正規表達式的符號

符號 意思 例子 符合
^word word 在行首 ^# 以 # 開頭的註解行
word$ word 在行尾 驚嘆號後面接行尾符號 以 ! 結尾的行
^$ 行首緊接著行尾 (同左) 空白行
. 任意 1 個字元 c.t cat、cut(不含 ct)
* 前一個字元重複 0 次以上 ca*t ct、cat、caaat
[abc] 括號內任一個字元 c[au]t cat、cut
[a-z]、[0-9] 範圍內任一個字元 [0-9] 含有數字的行
[^a-z] 不在括號內的任一個字元(^ 寫在括號內的第一個) ^[^#] 不是以 # 開頭的行
[[:digit:]] 字元類別:數字;還有 [:upper:]、[:lower:]、[:alpha:]、[:space:] [[:upper:]] 含有大寫字母的行
\{n,m\} 前一個字元連續 n 到 m 次(\{n\} 剛好 n 次、\{n,\} n 次以上) a\{2,\} caaat

投影片的行首行尾範例:

grep -n '^#' regular_express.txt     # 以 # 開頭的行,並列出行號
grep -n '!$' regular_express.txt     # 以 ! 結尾的行
grep -n '^$' regular_express.txt     # 空白行

幾個容易混淆的地方:

  • ^ 寫在中括號外面是行首,寫在中括號裡的第一個是「不在範圍內」。
  • * 不是「任意字元」,而是前一個字元的次數,所以 ca*t 連 ct 都符合;要表示「任意多個任意字元」寫 .*。
  • [[:digit:]] 外面那層中括號是集合,裡面的 [:digit:] 是類別名稱,兩層都要寫。字元類別會依語系設定判斷, 寫起來比 [0-9]、[a-z] 長,但意思比較明確。

基本與延伸正規表達式

grep 預設使用基本正規表達式(BRE)。在 BRE 裡,+、?、|、(、)、{、} 都是一般字元, 要在前面加反斜線才有特殊意義(GNU grep 的寫法):

意思 基本(grep) 延伸(grep -E,下一課)
前一個字元 1 次以上 ca\+t ca+t
前一個字元 0 或 1 次 colou\?r colou?r

「或」也一樣:基本正規表達式寫成 cat\|dog,延伸正規表達式直接寫 cat|dog。

所以 grep 'ca+t' 找的是「c、a、加號、t」這四個字元,通常什麼都找不到。投影片的 grep "ab\|cd" 就是 BRE 的「或」。 下一課的 grep -E(舊寫法 egrep)使用延伸正規表達式,這些符號就不必加反斜線。

互動練習:行首行尾、. 與 *、集合

對應投影片的 Lab03。regular_express.txt 有註解行、空白行、驚嘆號結尾的行,以及 cat、cut、ct、caaat 等相近的字。每顆按鈕都加了 -n,從行號看出哪幾行符合;比較 ca+t 與 ca\+t 的結果,就看得出基本正規表達式的反斜線規則。這個模擬器還不支援 [[:digit:]] 這類字元類別,請在真的終端機上試。

想一想:grep 的選項

1.

要列出 /etc/passwd 裡不含 nologin 的行,並顯示行號,應該用哪一個?

2.

grep -c error app.log 與 grep error app.log | wc -l 的結果有什麼關係?

3.

要在 /etc 底下所有檔案裡找 PermitRootLogin,而且不想看到 Permission denied,哪一個寫法正確?

第 1 次作答

想一想:正規表達式的符號

1.

grep '^[^#]' sshd_config 會列出哪些行?

2.

下列哪一個字串不符合正規表達式 go*d?

3.

在 grep(沒有加 -E)裡想找 ab 或 cd,哪一個寫法正確?

第 1 次作答

重點精華回顧

  • grep '樣式' 檔案 印出符合的整行;-i 不分大小寫、-n 行號、-v 反向、-c 計數、-r 遞迴搜尋目錄、-l 只列檔名。
  • grep 可以放在管線中篩選其他指令的輸出,再接 wc -l 計算行數;2>/dev/null 丟掉 Permission denied。
  • 樣式用單引號包起來;正規表達式的 *、.、? 和萬用字元意思不同。
  • ^ 是行首,$ 是行尾;兩個連在一起就是空白行。
  • . 任意一個字元、* 前一個字元 0 次以上、.* 任意多個任意字元。
  • [abc] 其中一個、[a-z] 範圍、[^a-z] 不在範圍內、[[:digit:]] 字元類別、\{n,m\} 重複次數。
  • 基本正規表達式(grep)的 +、?、| 要寫成 \+、\?、\|;下一課的 grep -E 不必加反斜線。
這一課完成了!
Next lesson:延伸正規表達式
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。