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 13

檔案搜尋

which、whereis、萬用字元與 find
Interaction 20 minute(s)

Learning Objectives

  • 用 which 依 PATH 找出指令的完整路徑,用 type 分辨內建指令與別名,用 whereis 一併找出說明文件
  • 用萬用字元 *、?、[ ] 比對檔名,了解萬用字元是由 shell 先展開
  • 用 find 依名稱、類型、大小與修改時間搜尋檔案
  • 看懂 find 的 Permission denied 訊息並用 2>/dev/null 隱藏,知道 locate 的用法與前置步驟

檔案在哪裡?

Linux 的檔案系統從 / 開始,底下有數十萬個檔案。要找東西時,先想清楚找的是哪一種:

想找的 工具 怎麼找
某個指令的執行檔在哪裡 which、type、whereis 只看幾個固定的目錄,很快
檔名符合某個樣式的檔案(目前目錄或指定目錄) 萬用字元 *、?、[ ] 由 shell 比對一層目錄裡的檔名
符合條件的檔案(名稱、類型、大小、時間),可以往下搜尋整個目錄樹 find 實際走過每一個目錄,最完整也最慢
檔名含有某段文字的檔案(全系統) locate 查事先建好的資料庫,很快,但資料可能不是最新

which、type 與 whereis:找指令

輸入 top 時,shell 會依照環境變數 PATH 列出的目錄,一個一個找名為 top 的執行檔:

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
$ which top
/usr/bin/top
$ which history
$

which 只在 PATH 的目錄裡找執行檔。history 沒有任何輸出,因為它不是一個檔案,而是 bash 的內建指令(builtin)。 which -a 會列出 PATH 裡所有同名的執行檔,而不是只列第一個。

type 是 bash 的內建指令,連別名與內建指令都認得,是確認「這個指令到底是什麼」最直接的方法:

$ type history
history is a shell builtin
$ type ll
ll is aliased to `ls -alF'
$ type ls
ls is /usr/bin/ls

whereis 除了執行檔,還會找出說明文件(man page)等相關檔案:

$ whereis top
top: /usr/bin/top /usr/share/man/man1/top.1.gz

whereis 只查幾個固定的系統目錄(whereis -l 會列出它查哪些目錄),不會搜尋整個系統;-b 只找執行檔、-m 只找說明文件。

萬用字元:比對檔名

符號 意思 例子 符合
* 0 個到任意多個字元 report*.txt report.txt、report1.txt、report10.txt
? 剛好 1 個任意字元 report?.txt report1.txt、report2.txt(不含 report10.txt)
[abc] 括號內任一個字元 [rR]eport* report1.txt、Report.txt
[0-9] 範圍內任一個字元 *[0-9]* 檔名含有數字的
[!a-z] 不在括號內的任一個字元 [!a-z]* 不是以小寫字母開頭的

投影片寫的 [^a-z] 在 bash 裡也代表「不在括號內」;POSIX 標準的寫法是 [!a-z],在各種 shell 都通用。

萬用字元由 shell 展開,不是由指令處理。 執行 ls -d /etc/cron* 時,bash 先把 /etc/cron* 換成所有符合的檔名, ls 收到的其實是 ls -d /etc/cron.d /etc/cron.daily /etc/cron.weekly /etc/crontab。用 echo 就看得到展開的結果:

$ echo *.txt
Report.txt report1.txt report10.txt report2.txt
$ echo '*.txt'
*.txt

加上引號,shell 就不展開,原樣交給指令。沒有任何檔名符合時,bash 預設也會原樣保留, 所以投影片才會看到 ls: /etc/[^a-z]*: No such file or directory 這種訊息。

投影片的 ll -d 是 ls -alF 的別名再加上 -d;-d 讓 ls 列出目錄本身,而不是目錄裡面的內容。

投影片也提到三種引號:單引號 '…' 裡的內容完全原樣;雙引號 "…" 裡的 $變數 會被替換; 反引號 `指令` 會先執行裡面的指令、再把輸出放回原處,例如 echo "現在時間 `date`"。 反引號在 bash 裡建議改寫成 $(date),可以巢狀使用、也比較不容易和單引號看錯。

互動練習:找指令與萬用字元

對應投影片的 Lab01、Lab02。家目錄裡有 report1.txt、report2.txt、report10.txt、Report.txt 四個檔案,比較 ?、*、[ ] 各會比對到哪些;最後用 echo 看看萬用字元展開前後的差別。按鈕都是真的指令。

find:依條件搜尋整個目錄樹

find 起點目錄 條件…

find 從起點目錄開始,走過底下的每一層目錄,列出符合所有條件的項目;不給條件就列出全部。

條件 意思 例子
-name '樣式' 檔名符合樣式(可用 *、?、[ ],區分大小寫) find /etc -name 'cron*'
-iname '樣式' 同上,但不分大小寫 find . -iname '*.txt'
-type f/d/l 一般檔案/目錄/符號連結 find /etc -type d -name 'cron*'
-size +5M 大於 5 MB(-5M 小於;單位有 k、M、G) find /usr/bin -size +5M
-mtime -7 內容在 7 天內修改過(+7=超過 7 天前) find ~ -type f -mtime -7
-atime -7 7 天內被讀取過(投影片的例子) find /usr/bin -type f -atime -7
-maxdepth 1 最多往下找幾層 find /etc -maxdepth 1 -name '*.conf'

-name 後面的樣式一定要加引號。 不加引號時,shell 會先用目前目錄的檔名把 *.txt 展開。 如果目前目錄剛好只有一個 todo.txt,find . -name *.txt 實際執行的是 find . -name todo.txt, 只會找到叫 todo.txt 的檔案,而且不會有任何錯誤訊息提醒你;有好幾個符合時則會出現 paths must precede expression 錯誤。

Permission denied

一般使用者沒有權限進入某些目錄(例如 /root、/etc/ssl/private),find 走到那裡就會印出錯誤訊息:

$ find /etc -name '*.key'
find: '/etc/ssl/private': Permission denied

這些訊息走的是標準錯誤,可以用上一課的 2>/dev/null 丟掉,只留下找到的結果;需要完整結果時,改用 sudo find …。

延伸:locate

locate 不走訪目錄,而是查一個事先建好的檔名資料庫,所以速度很快。Ubuntu 提供的實作是 plocate,Server 版預設沒有安裝:

sudo apt install plocate      # 安裝
sudo updatedb                 # 建立/更新資料庫
locate crontab                # 列出路徑中含有 crontab 的檔案

資料庫是定期更新的,剛建立的檔案要等下一次 updatedb 之後才找得到;要找最新的狀態,還是用 find。

互動練習:用 find 搜尋檔案

對應投影片的 Lab03。家目錄裡有 todo.txt、notes/ 與 projects/,/usr/bin 裡有一個大於 5 MB 的執行檔。先故意不加引號執行一次 find . -name *.txt,和加了引號的結果比較;再依類型、大小與修改時間搜尋,最後處理 Permission denied,並試試 locate 需要的前置步驟。

想一想:找指令與萬用字元

1.

which cd 沒有任何輸出,最可能的原因是?

2.

目錄裡有 a1.log、a2.log、a10.log、b1.log。ls a?.log 會列出哪些?

3.

目前目錄有 x.txt 與 y.txt。執行 echo *.txt 時,echo 實際收到的參數是?

第 1 次作答

想一想:find 與 locate

1.

要找出 /var/log 底下所有副檔名是 .log 的一般檔案,哪一個寫法正確?

2.

find / -name passwd 印出很多 Permission denied,只想看找到的結果,應該怎麼改?

3.

剛建立 report.pdf,馬上執行 locate report.pdf 卻找不到,原因是?

第 1 次作答

重點精華回顧

  • which 依 PATH 找執行檔,找不到內建指令;type 能分辨別名、內建指令與執行檔;whereis 同時找執行檔與說明文件。
  • 萬用字元:* 任意多個字元、? 剛好一個字元、[abc] 其中一個、[0-9] 範圍、[!a-z] 不在範圍內。
  • 萬用字元由 shell 先展開,指令收到的是展開後的檔名;加上引號就不展開。
  • find 起點 條件:-name/-iname 名稱、-type f/d/l 類型、-size +5M 大小、-mtime -7 修改時間、-maxdepth 層數;-name 的樣式要加引號。
  • 沒有權限的目錄會出現 Permission denied,用 2>/dev/null 隱藏,或用 sudo find。
  • locate 查資料庫很快,Ubuntu Server 要先 sudo apt install plocate 再 sudo updatedb。
這一課完成了!
Next lesson:正規表達式
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。