National Yang Ming Chiao Tung 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 70 24°C PM2.5 19
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 15

延伸正規表達式

egrep 的 + ? | ()、sed 取代與 awk 欄位
Interaction 20 minute(s)

Learning Objectives

  • 用 grep -E(egrep)搭配延伸正規表達式的 +、?、|、() 搜尋文字
  • 用 sed 的 s/// 取代文字,用 g 旗標取代一行中所有符合的地方,用 Nd 刪除行、用 -n 搭配 p 只印出指定的行
  • 知道 sed -i 會直接修改檔案,修改前先不加 -i 看一次結果
  • 用 awk 印出指定的欄位,用 -F 指定分隔字元,用條件只處理符合的行

從「找到」到「改掉」與「挑出欄位」

上一課用 grep 與基本正規表達式(BRE)找出符合條件的行。這一課往前走三步:

工具 做什麼 例子
grep -E(egrep) 用延伸正規表達式(ERE)搜尋,可以用的特殊符號比上一課更多 找出 god、good、goood
sed 串流編輯器(stream editor):逐行讀入、取代或刪除,再輸出 把 cpu 全部改成 memory
awk 把每一行切成欄位,挑出需要的欄位,或只處理符合條件的行 從 /etc/passwd 列出帳號與 UID

awk 的名稱來自三位作者 Alfred Aho、Peter Weinberger、Brian Kernighan 的姓氏字首,它本身是一個直譯式的小語言,很適合處理一行一筆的文字資料。

這三個工具預設都不會修改原本的檔案:結果印在螢幕上,要存下來就用上一課的重導 >。只有 sed -i 會直接改檔案(本課後半說明)。

延伸正規表達式的四個符號

以下用一個練習檔 ere.txt 說明,內容是:

(gd)
(god)
(good)
(goood)
(glad)
  • +:前一個字元出現一次以上。egrep -n 'go+d' ere.txt 找到 2:(god)、3:(good)、4:(goood)
  • ?:前一個字元出現零次或一次。egrep -n 'go?d' ere.txt 找到 1:(gd)、2:(god)
  • |:或,符合其中一個即可。egrep -n 'gd|good' ere.txt 找到 1:(gd)、3:(good)
  • ():群組,把幾個字元當成一個單位。egrep -n 'g(la|oo)d' ere.txt 找到 3:(good)、5:(glad)

注意 go+d 不會找到 (gd):+ 要求 o 至少出現一次;go?d 也不會找到 (good):? 最多只允許一個 o。

egrep 與 grep -E

egrep 就是 grep -E。GNU grep 從 2.5.3 版(2007 年)起就把 egrep 列為不建議使用,3.8 版起執行 egrep 會先印出警告:

egrep: warning: egrep is obsolescent; using grep -E

Debian 與 Ubuntu 的 grep 套件把這個警告拿掉了,所以在 Ubuntu 24.04 執行 egrep 不會看到它,但寫腳本時請改用 grep -E,換到別的發行版本也能正常執行。

不加 -E 時這些符號是什麼?

在基本正規表達式(BRE,grep 不加 -E)裡,+、?、|、() 都是普通字元。grep -n 'go+d' ere.txt 找的是「g、o、加號、d」這四個字,所以什麼都找不到。GNU grep 的 BRE 要在前面加反斜線 \+、\?、\|、\(\) 才有特殊意義。與其記兩套寫法,需要這些符號時直接加 -E 比較清楚。

互動練習:用 egrep 搜尋 ere.txt

對應投影片 Lab01。每一顆按鈕都是真的指令,按下去會在終端機看到結果。先看一次 ere.txt 的內容,再依任務試 +、?、|、();其中有一顆按鈕故意不加 -E,看看 + 變成普通字元時會找到什麼。

sed:取代、刪除、只印出指定的行

sed 一次讀一行,依照你給的指令處理後輸出。最常用的寫法:

sed [選項] '[位址]指令' 檔案
寫法 作用
sed 's/cpu/memory/' test.txt 每一行第一個 cpu 改成 memory(區分大小寫)
sed 's/cpu/memory/g' test.txt g 旗標:一行裡所有的 cpu 都改
sed '1,8 s/[Tt]he/THE/g' MyFile.txt 只處理第 1~8 行,[Tt] 讓 the 與 The 都符合
sed '1d' test.txt 刪除第 1 行;'2,3d' 刪除第 2~3 行,'/^#/d' 刪除以 # 開頭的行
sed -n '2p' test.txt -n 關掉「自動印出每一行」,只印出 p 指定的行
sed -n '/cpu/p' test.txt 只印出含有 cpu 的行,效果類似 grep cpu
sed -e 's/a/b/' -e '3d' 檔案 -e 可以一次給好幾個指令

加上 -E(舊寫法 -r)就能在 sed 裡使用延伸正規表達式,例如用括號群組與 \1(第一組括號比對到的文字)把 cpu 或 memory 用中括號標出來:

sed -E 's/(cpu|memory)/[\1]/g' test.txt

用投影片的對照來記:s 是命令、cpu 是要找的樣板、memory 是取代成的文字、g 是旗幟、1,8 是位址。

sed -i 會直接改檔案

sed -i 's/cpu/CPU/g' test.txt

加上 -i(in-place)後,sed 不再把結果印到螢幕,而是直接寫回檔案,沒有確認、也沒有復原。建議的習慣:

  1. 先不加 -i 執行一次,確認螢幕上的結果正確
  2. 再加上 -i;重要的檔案先複製一份,或寫成 sed -i.bak ...,GNU sed 會把原檔另存成 test.txt.bak

系統設定檔(例如 /etc 底下)屬於 root,修改時要 sudo sed -i ...,下手前更要先看過結果。

awk:挑出欄位與條件篩選

awk 預設用空白(空格或 Tab)把每一行切成欄位:$1 是第一欄、$2 是第二欄,$0 是整行,NF 是這一行的欄位數,NR 是行號。

ls -l /usr | awk '{print $4,$9}'

ls -l 的第 4 欄是群組、第 9 欄是名稱,所以會印出 root bin、root lib⋯⋯。print 裡的逗號會輸出成一個空格。 第一行 total 16 只有兩欄,第 4、9 欄是空的,所以印出一個空白行。

寫法 作用
awk -F: '{print $1}' /etc/passwd -F: 用冒號分欄,印出每個帳號名稱(投影片寫成 -F":",效果相同)
awk -F: '{print $1,$3}' /etc/passwd 印出帳號與 UID,例如 root 0、student 1000
awk -F: '$3>=1000 {print $1}' /etc/passwd 只處理第 3 欄大於等於 1000 的行:Ubuntu 一般使用者的 UID 從 1000 開始
awk '/cpu/ {print NR, $0}' test.txt 只處理含有 cpu 的行,印出行號與整行

條件寫在大括號前面,動作寫在大括號裡面;整段程式要用單引號包起來,避免 $1 被 shell 當成變數展開。 在真的 Ubuntu 上,$3>=1000 還會列出 nobody:它的 UID 是 65534,是系統保留的帳號,不是真的使用者。

互動練習:用 sed 取代與刪除,用 awk 挑欄位

對應投影片 Lab02 與 Lab03。test.txt 有三行文字;前幾顆 sed 按鈕只會把結果印在螢幕上,按 cat test.txt 可以確認檔案沒變。加了 -i 的那一顆才會真的改檔案,按完再看一次 cat。最後用 awk 從 /etc/passwd 挑出欄位。

想一想:延伸正規表達式

1.

ere.txt 有 (gd)、(god)、(good)、(goood)、(glad) 五行,grep -E 'go+d' ere.txt 會找到幾行?

2.

想同時找出含有 error 或 warning 的行,下列哪一個寫法正確?

3.

在 Ubuntu 24.04 執行 egrep 時,下列說法何者正確?

第 1 次作答

想一想:sed 與 awk

1.

某一行是 cat cat cat,執行 sed 's/cat/dog/' 後這一行變成什麼?

2.

執行 sed 's/8080/9090/g' config.txt 之後,config.txt 的內容會怎樣?

3.

/etc/passwd 的一行是 student:x:1000:1000:Student,,,:/home/student:/bin/bash,awk -F: '{print $1,$6}' 會印出什麼?

第 1 次作答

重點精華回顧

  • 延伸正規表達式(grep -E):+ 一次以上、? 零次或一次、| 或、() 群組;不加 -E 時這些符號是普通字元。
  • egrep 等於 grep -E,已被列為淘汰的寫法;腳本裡請寫 grep -E。
  • sed 's/舊/新/' 取代每一行第一個符合處,加 g 取代全部;Nd 刪除第 N 行,-n 搭配 p 只印出指定的行,-E 使用延伸正規表達式。
  • sed 預設不改檔案;sed -i 直接寫回檔案,先不加 -i 確認結果,必要時用 -i.bak 留備份。
  • awk '{print $1,$3}' 印出指定欄位,-F: 指定分隔字元,'$3>=1000 {...}' 只處理符合條件的行。
這一課完成了!
Next lesson:檔案打包與壓縮
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。