Linux find 指令:-name、-type、-size、-mtime 選項與 -delete
Linux find 指令會從起始資料夾依序往下走訪,尋找符合名稱、類型、大小、修改時間、權限等條件的檔案與資料夾。我們在 AlmaLinux 9 伺服器的測試資料夾建立十個檔案,逐一變更條件執行後,把找到的檔案以 -exec 交給其他指令,刪除時則在 -delete 之前先以相同條件確認清單。
結果框中的測試資料夾路徑和檔案時間已換成範例值。Debian 12 伺服器的輸出只有家目錄路徑是範例值,其餘維持原樣。
1️⃣ Linux find 指令用法
基本形式是 [find 起始路徑 條件]。不寫起始路徑時在目前資料夾(.)尋找,不寫條件時輸出起始路徑下的所有檔案和資料夾。
測試是在如下建立了三個資料夾和十個檔案的 /root/work 中進行。
find /root/work/root/work
/root/work/docs
/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/backup
/root/work/report.txt
/root/work/README.md
/root/work/run.sh
/root/work/my notes.txt
/root/work/empty.txtfind 不會依名稱排序結果;這次執行時,遇到 docs 資料夾就先輸出裡面所有檔案,然後才前往 logs。
連續寫多個條件時,只會留下全部符合的檔案。
-name 只拿檔案名稱和樣式比對,不看路徑。要用單引號包住樣式,Shell 才不會先展開 *,而是原樣交給 find。
find /root/work -name '*.txt'/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/report.txt
/root/work/my notes.txt
/root/work/empty.txt-name 區分大小寫。尋找以 readme 開頭的名稱只找到 docs/readme.txt,改用 -iname 後連 README.md 也找到了。
find /root/work -name 'readme*'/root/work/docs/readme.txtfind /root/work -iname 'readme*'/root/work/docs/readme.txt
/root/work/README.mdfind 包含在 findutils 套件中。以 [find --version] 第一行確認的版本,AlmaLinux 9.8 伺服器是 findutils 4.8,Debian 12 伺服器是 4.9。
2️⃣ find -type d 與 -maxdepth
-type f 只留下一般檔案,-type d 只留下資料夾。
find /root/work -type d/root/work
/root/work/docs
/root/work/logs
/root/work/backup起始路徑 /root/work 也是資料夾,所以一起出現在第一行。同一路徑改用 -type f 後,十個檔案全部出現。
-maxdepth 決定從起始路徑往下走幾層。以 -maxdepth 1 執行時,docs 和 logs 裡的檔案被排除,只留下 /root/work 正下方的五個檔案。
find /root/work -maxdepth 1 -type f/root/work/report.txt
/root/work/README.md
/root/work/run.sh
/root/work/my notes.txt
/root/work/empty.txt相反地,-mindepth 2 跳過起始路徑正下方,只輸出子資料夾內的五個檔案。
find /root/work -mindepth 2 -type f/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmpman 頁面建議把 -maxdepth 這類全域選項寫在起始路徑正後方。把 -maxdepth 1 寫在 -type f 後面,結果同樣是那五個檔案,但在終端機執行時會多一行警告,要求把全域選項寫在前面(依 man 頁面)。
只想確認資料夾或檔案有幾個時,就把輸出交給 wc -l。計算 -type d 的輸出得到 4,包含起始路徑。
find /root/work -type d | wc -l4加上 -maxdepth 1 也得到 4,因為三個子資料夾都在起始路徑正下方。
find /root/work -maxdepth 1 -type d | wc -l4以 -mindepth 2 只計算子資料夾內的檔案,得到 5。
find /root/work -mindepth 2 -type f | wc -l53️⃣ find 指令大小、時間條件
數字前的 + 代表大於該值,- 代表小於該值,沒有符號則是剛好等於該值。-size 的單位如下表(依 man 頁面)。
| 單位 | 大小 | 是 |
|---|---|---|
| c | 1 位元組 | -size +500c |
| k | 1,024 位元組 | -size +1k |
| M | 1,048,576 位元組 | -size +1M |
| G | 1,073,741,824 位元組 | -size +1G |
| (無) | 512 位元組區塊 | -size +2 |
find /root/work -type f -size +1k/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt大於 1k 的檔案是 5,000 位元組的 guide.txt、2MB 的 app.log 和 1,200 位元組的 report.txt。
find /root/work -size +1M/root/work/logs/app.log連寫兩次 -size 指定範圍後,只留下大於 1k 且小於 100k 的兩個檔案。
find /root/work -type f -size +1k -size -100k/root/work/docs/guide.txt
/root/work/report.txt-size 會把大小除以單位並無條件進位後再比較。因此 300 位元組的 README.md 也算 1k,-size -1k 只找到大小為 0 的 empty.txt。
find /root/work -type f -size -1k/root/work/empty.txt為了查看時間條件,我們把 README.md、my notes.txt、docs/readme.txt 的修改時間設為 3 天前,兩個 .tmp 檔案設為 10 天前。每個檔案都留有存取、修改、變更三種時間,可用 stat 指令確認。
-mtime 以 24 小時為單位計算最後修改至今的時間。-mtime -1 找到 24 小時內建立的五個檔案。
find /root/work -type f -mtime -1/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-mtime +7 只找到設為 10 天前的兩個 .tmp 檔案。刪除舊記錄檔或暫存檔之前確認清單時,就用這個形式。
find /root/work -type f -mtime +7/root/work/docs/old.tmp
/root/work/logs/cache.tmp-mmin 以分鐘做相同計算。-mmin -60 的結果與 -mtime -1 相同,是那五個檔案。
find /root/work -type f -mmin -60/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-newer 尋找比基準檔案更晚修改的檔案。以設為 3 天前的 README.md 為基準執行,找到之後建立的五個檔案。
find /root/work -type f -newer /root/work/README.md/root/work/docs/guide.txt
/root/work/logs/app.log
/root/work/report.txt
/root/work/run.sh
/root/work/empty.txt-empty 同時尋找大小為 0 的檔案和空資料夾,找到空資料夾 backup 和 empty.txt。
find /root/work -empty/root/work/backup
/root/work/empty.txt4️⃣ find -perm 與 -user 條件
-perm 後面只寫數字時,尋找權限剛好等於該值的檔案。-perm 644 排除了 755 的 run.sh 和 600 的 my notes.txt,找到八個。
find /root/work -type f -perm 644/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/report.txt
/root/work/README.md
/root/work/empty.txt在模式前加 -,尋找具備所寫全部權限的檔案。有擁有者執行權限(u+x)的檔案只有 run.sh。
find /root/work -type f -perm -u+x/root/work/run.sh在模式前加 /,只要有所寫權限之一就會找到。6000 是 SUID(4000)與 SGID(2000)相加的值,要在伺服器上檢查執行時會以擁有者或群組權限運作的檔案時使用這個形式。
測試資料夾沒有符合的檔案,因此替 run.sh 加上 SUID 後執行,找到 run.sh 一個。
chmod u+s /root/work/run.shfind /root/work -type f -perm /6000/root/work/run.sh舊資料在同一位置寫的是 -perm +6000。照樣執行後以 [invalid mode] 錯誤結束。
find /root/work -type f -perm +6000find: invalid mode '+6000'+ 形式已在 findutils 4.5.12 移除(依 man 頁面),所以要寫 /。測試用的 SUID 已用 chmod u-s 移除。
-user 依擁有者名稱篩選。測試檔案全部屬於 root,光用 -user root 篩不掉任何檔案,再以 -name 一起選 .log 檔案後,只找到 app.log。
find /root/work -type f -user root -name '*.log'/root/work/logs/app.log-nouser 尋找擁有者 UID 沒有對應帳號的檔案。用於檢查刪除帳號後留在伺服器上的檔案,把 guide.txt 的擁有者改為不存在的 UID 1500 後執行,找到 guide.txt。
chown 1500:1500 /root/work/docs/guide.txtfind /root/work -nouser/root/work/docs/guide.txt-nogroup 對擁有群組做相同檢查,找到群組也改為 1500 的 guide.txt。測試結束後以 chown root:root 還原。
條件之間加入 -o,只要符合其中之一即可。與 -type f 這類其他條件一起使用時,要用括號包住以 -o 連接的條件。括號前要加反斜線以免 Shell 先解譯,括號與條件之間要留空格。
find /root/work -type f \( -name '*.log' -o -name '*.tmp' \)/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp! 會反轉緊接在後的條件。尋找不是 .txt 的檔案,找到五個。
find /root/work -type f ! -name '*.txt'/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp
/root/work/README.md
/root/work/run.sh5️⃣ find -exec 活用
-exec 會對每個找到的檔案執行後面寫的指令,找到的路徑放進 {} 的位置。指令結尾的 ; 要寫成 \;,以免 Shell 先當成指令分隔符號讀取。
find /root/work -name '*.log' -exec ls -l {} \;-rw-r--r-- 1 root root 2097152 Oct 5 14:10 /root/work/logs/app.log結尾以 + 代替 \; 時,會把找到的路徑集中起來,只執行一次指令。用 echo 比較,\; 把五個路徑輸出成五行,+ 則輸出成一行。
find /root/work -name '*.txt' -exec echo {} \;/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/report.txt
/root/work/my notes.txt
/root/work/empty.txtfind /root/work -name '*.txt' -exec echo {} +/root/work/docs/readme.txt /root/work/docs/guide.txt /root/work/report.txt /root/work/my notes.txt /root/work/empty.txtls 這類可接收多個路徑的指令,用 + 集中以減少執行次數。我們以一次 ls -lh 確認了大於 1k 的三個檔案大小。
find /root/work -type f -size +1k -exec ls -lh {} +-rw-r--r-- 1 root root 4.9K Oct 5 14:10 /root/work/docs/guide.txt
-rw-r--r-- 1 root root 2.0M Oct 5 14:10 /root/work/logs/app.log
-rw-r--r-- 1 root root 1.2K Oct 5 14:10 /root/work/report.txt要把網站資料夾權限批次整理成檔案 644、資料夾 755 時,也使用 -exec。執行前先找出值不同的檔案,找到 run.sh 和 my notes.txt。
find /root/work -type f ! -perm 644/root/work/run.sh
/root/work/my notes.txt檔案和資料夾權限不同,所以分成 -type f 和 -type d 執行。chmod 也能接收多個路徑,因此結尾寫 +。
find /root/work -type f -exec chmod 644 {} +find /root/work -type d -exec chmod 755 {} +以相同條件再次尋找時什麼都沒有,事先改為 700 的 backup 資料夾也變成 755。
但 run.sh 也變成 644,執行時出現 [Permission denied]。在混有執行檔的資料夾中,要用 chmod +x 重新賦予這些檔案執行權限。
只變更子資料夾內的檔案時加上 -mindepth 2。以 660 執行後只有 docs 和 logs 內的五個檔案改變,確認後還原為 644。
find /root/work -mindepth 2 -type f -exec chmod 660 {} +find /root/work -type f -perm 660/root/work/docs/readme.txt
/root/work/docs/guide.txt
/root/work/docs/old.tmp
/root/work/logs/app.log
/root/work/logs/cache.tmp串接 find 與 grep,就能依檔案內容而非名稱尋找。為了模擬檢查 Web Shell 的情況,我們在另一個資料夾 /root/site 建立四個 PHP 檔案,並在 upload 資料夾的 html-cache.php 和 photo.php 放入 [base64_decode] 字串。
find /root/site -type f/root/site/lib/func.php
/root/site/upload/html-cache.php
/root/site/upload/photo.php
/root/site/index.php知道 Web Shell 名稱時用 -name 尋找。加上 -maxdepth 2 只往下到上傳資料夾,找到 html-cache.php 一個。
find /root/site -maxdepth 2 -type f -name 'html*.php'/root/site/upload/html-cache.php改成普通名稱的 photo.php 這裡沒有出現。以 -exec 執行 grep -l 時只輸出含該字串的檔案路徑,兩個檔案都出現了。
find /root/site -name '*.php' -exec grep -l 'base64_decode' {} +/root/site/upload/html-cache.php
/root/site/upload/photo.php[base64_decode] 也是 WordPress 核心檔案中會出現的函式,因此找到的檔案要開啟確認內容後再刪除。
-print0 在路徑之間放入空字元而非換行。空字元在畫面上看不見,所以路徑連成一行輸出。
find /root/work -name '*.txt' -print0/root/work/docs/readme.txt/root/work/docs/guide.txt/root/work/report.txt/root/work/my notes.txt/root/work/empty.txt要把 my notes.txt 這類含空格的名稱不中斷地交給其他指令時,-print0 的輸出要與 xargs -0 成對使用(依 man 頁面)。
6️⃣ find 指令刪除檔案與選項
刪除之前,先不刪除地執行相同條件,親眼確認要刪除的檔案清單。
find /root/work -name '*.tmp'/root/work/docs/old.tmp
/root/work/logs/cache.tmp-exec rm {} \; 對每個找到的檔案執行 rm。只在 logs 資料夾執行後,cache.tmp 被刪除,只留下 app.log。
find /root/work/logs -name '*.tmp' -exec rm {} \;ls /root/work/logsapp.log-delete 由 find 直接刪除,不另外執行 rm。執行時沒有任何輸出,以相同條件再次尋找時已沒有 .tmp 檔案。
find /root/work -name '*.tmp' -deletefind /root/work -name '*.tmp'-delete 一定要寫在條件後面。find 從前面起把指令行當成一個運算式計算,因此把 -delete 放在最前面,會在看條件之前就試圖刪除起始路徑下的所有內容(依 man 頁面)。
🔲 find 指令選項
📌 主要選項:本文實際執行的結果
| 選項 | 作用 | 本文看到的結果 | |
|---|---|---|---|
| -name | 尋找名稱符合樣式的項目(區分大小寫) | '*.txt' 找到五個檔案,'readme*' 只找到 docs/readme.txt | |
| -iname | 不分大小寫比對名稱 | 'readme*' 同時找到 docs/readme.txt 和 README.md | |
| -type f, -type d | 只留下一般檔案(f)或資料夾(d) | -type d 找到含起始路徑的四個資料夾,-type f 找到十個檔案 | |
| -maxdepth | 從起始路徑只往下走 N 層 | -maxdepth 1 只留下 /root/work 正下方的五個檔案 | |
| -mindepth | 跳過起始路徑往下 N 層以上的部分 | -mindepth 2 只找到子資料夾內的五個檔案 | |
| -size | 依大小尋找(+ 較大,- 較小,單位 c、k、M、G) | -size +1k 找到三個檔案,-size -1k 只找到 empty.txt | |
| -mtime | 以 24 小時為單位比較內容修改後經過的時間 | -mtime -1 找到五個檔案,-mtime +7 找到兩個 .tmp 檔案 | |
| -mmin | 以分鐘比較修改後經過的時間 | -mmin -60 找到與 -mtime -1 相同的五個檔案 | |
| -ctime | 以 24 小時為單位,比較到權限、名稱等檔案資訊變更的時間 | -ctime -1 找到全部十個測試檔案 | |
| -newer | 尋找比基準檔案更晚修改的項目 | 以設為 3 天前的 README.md 為基準找到五個檔案 | |
| -empty | 尋找大小為 0 的檔案和空資料夾 | 找到空資料夾 backup 和 empty.txt | |
| -perm | 依權限尋找(只寫數字為剛好該值,前加 - 為含全部所寫權限,前加 / 為含其一) | -perm 644 找到八個檔案,-perm -u+x 找到 run.sh,加上 SUID 後 -perm /6000 找到 run.sh | |
| -user | 依擁有者名稱篩選 | 與 -name '*.log' 一起寫時找到 app.log,不存在的帳號以錯誤結束 | |
| -nouser | 尋找擁有者 UID 沒有對應帳號的項目 | 找到 UID 改為 1500 的 guide.txt | |
| -nogroup | 尋找群組 GID 沒有對應群組的項目 | 找到 GID 改為 1500 的 guide.txt | |
| -o | 符合其一就留下(與其他條件併用時以括號包住) | 組合 .log 和 .tmp 找到三個檔案 | |
| ! | 反轉緊接在後的條件 | 找到五個不是 .txt 的檔案 | |
| -exec | 對每個找到的檔案執行後面的指令(路徑放在 {} 位置) | 結尾寫 + 時,一次 echo 把五個路徑輸出成一行 | |
| -print0 | 在路徑之間放入空字元而非換行 | 空字元看不見,路徑連成一行 | |
| -delete | 由 find 直接刪除找到的檔案(寫在條件後面) | 沒有任何輸出,再次尋找已無 .tmp 檔案 |
📎 其他選項:依官方手冊
| 選項 | 作用 | |
|---|---|---|
| -L | 跟隨符號連結並使用目標檔案的資訊 | |
| -path | 以整個路徑而非名稱與 Shell 樣式比對 | |
| -regex | 以整個路徑與規則運算式比對 | |
| -iregex | 像 -regex 一樣比對但不分大小寫 | |
| -atime | 以 24 小時為單位比較最後存取後經過的時間 | |
| -cnewer | 尋找狀態變更時間晚於基準檔案修改時間的項目 | |
| -group | 依擁有群組名稱篩選 | |
| -xdev | 不進入其他檔案系統的資料夾 | |
| -daystart | 時間條件從今天 0 點起算,而非 24 小時前 | |
| -prune | 若是資料夾就不往下走 | |
| -quit | 立即結束(-print -quit 只輸出第一個結果) | |
| -printf | 以自訂格式輸出 | |
| -ls | 以 ls -dils 形式輸出找到的檔案 | |
| -fprint | 把找到的路徑存到指定檔案而非畫面 | |
| -ok | 像 -exec 一樣執行,但先詢問 | |
| -execdir | 像 -exec 一樣執行,但在找到的檔案所在資料夾執行指令 |
🔲 find 指令模擬器
下面的模擬器是裝有相同測試資料夾的虛擬伺服器,可親自執行 -name、-type、-size、-mtime、-perm 等條件以及 -exec、-delete。
7️⃣ find 指令錯誤
樣式不加引號時,Shell 會依 萬用字元 規則先展開成目前資料夾的檔案名稱。移到 docs 資料夾後不加引號寫 *.txt,Shell 就換成 guide.txt 和 readme.txt 兩個名稱傳遞,出現下面的錯誤。
cd /root/work/docsfind /root/work -name *.txtfind: paths must precede expression: `readme.txt'
find: possible unquoted pattern after predicate `-name'?結束代碼是 1。
-exec 結尾漏掉 \; 或 + 時,出現 [missing argument to `-exec’]。
find /root/work -name '*.log' -exec ls -l {}find: missing argument to `-exec'起始路徑不存在時出現 [No such file or directory],以結束代碼 1 結束。
find /nonexistent-dir -name xfind: '/nonexistent-dir': No such file or directory-user 寫了不存在的帳號名稱,或 -type 寫了 f、d 等規定字母以外的值時,也不會開始尋找,立即以錯誤結束。
find /root/work -user nosuchuserfind: 'nosuchuser' is not the name of a known userfind /root/work -type xfind: Unknown argument to -type: x我們在 Debian 12 伺服器上以一般帳號把測試資料夾內 lock 資料夾的權限改為 000,再尋找測試資料夾。無法讀取 lock 內部,出現 [Permission denied],結束代碼是 1。
find ~/findtest/home/user/findtest
/home/user/findtest/report.txt
/home/user/findtest/lock
find: '/home/user/findtest/lock': Permission denied
/home/user/findtest/docs
/home/user/findtest/docs/readme.txt只想隱藏錯誤行時,就加上 2>/dev/null。錯誤文字消失了,但結束代碼仍然是 1。
find ~/findtest -name '*.txt' 2>/dev/null/home/user/findtest/report.txt
/home/user/findtest/docs/readme.txt🔢 FAQ & 推薦內容
ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。