SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-13

rm -rf 誤刪檔案如何復原?ext4 救援方法

誤執行 rm -rf 後,先停止磁碟寫入並卸載或唯讀掛載;本文依序說明 ext4 上實際可行的復原選項,以及為何成功率幾乎為零。

前 60 秒該做什麼

使用 rm -rf 復原已刪除的檔案時,能否成功取決於 2 件事,而且這 2 件事都必須在開啟搜尋引擎前完成。停止寫入該檔案系統。接著將它停止使用,可卸載檔案系統,或重新以唯讀模式掛載。

rm 不會抹除任何資料。它會移除目錄項目,然後將 inode 和檔案的資料區塊標記為可用。這些位元組仍留在裝置上。只有在區塊配置器將這些區塊交給其他用途,且該用途將資料寫入其中後,原本的資料才會消失。檔案系統每多掛載並忙碌 1 秒,daemon 就可能寫入 1 行日誌,或資料庫可能 flush 1 個頁面;其中任一寫入都可能覆寫你要復原的區塊。

因此,最先執行的指令應該是停止寫入,而不是復原檔案。

sudo systemctl stop nginx postgresql
sudo umount /mnt/data

如果 umount 回應 umount: /mnt/data: target is busy.,請找出是哪個程序仍在使用該檔案系統。

sudo fuser -vm /mnt/data
sudo lsof +D /mnt/data

如果無法釋放該檔案系統,請改以唯讀模式重新掛載。唯讀掛載會停止新的配置,這已涵蓋你大部分的需求。

sudo mount -o remount,ro /mnt/data

如果已刪除的路徑位於 root filesystem,處理會更困難。sudo mount -o remount,ro / 通常會因 mount: /: cannot remount /dev/vda1 read-only. 而失敗,因為執行中的程序會以寫入模式開啟檔案,而 kernel 不會強制關閉這些檔案。在 VPS 上,實際可行的做法是使用供應商提供的 rescue 或 recovery mode:系統會啟動獨立的 live system,並將磁碟連接到該系統,但不會掛載磁碟。接下來的每個指令都會針對沒有任何程序寫入的裝置執行。

本指南全篇都適用同一項規則。不要將復原的檔案、磁碟映像或新安裝的工具寫入你要復原的檔案系統。請連接第 2 個 volume,或透過 SSH 將輸出傳送到另一台機器。

為何在 ext4 上使用 rm -rf 復原幾乎沒有希望

安裝任何工具前,先調整預期。確認目前使用的檔案系統:

lsblk -f

ext4 是幾乎所有 VPS 映像檔的預設檔案系統。檔案的資料位置會以 extent tree 儲存在 inode 中。extent 是一筆記錄,表示此檔案的邏輯區塊 N 從實體區塊 M 開始,連續使用 L 個區塊。小型檔案會直接在 inode 內儲存最多 4 筆記錄。較大的檔案則會指向額外的區塊,由這些區塊儲存樹狀結構的其餘部分。

檔案的最後一個連結消失時,ext4 會走訪該樹狀結構,將每個 extent 交還給區塊配置器,並清除 inode 中的樹狀結構。接著 inode 會被標記為可用,並寫入刪除時間。資料本身不會被修改,但唯一記錄資料位置的資訊已經遭到清除。

這就是它與 ext3 的差異。在 ext3 中,被刪除的 inode 仍會保留足夠資訊,讓類似 ext3grep 的工具可以追蹤資料。你仍可在 ext4 上列出已刪除的 inode:

sudo debugfs -R lsdel /dev/vdb1

除非傳入 -w,否則 debugfs 會以唯讀模式開啟裝置。因此,在未掛載的裝置上執行是安全的,嘗試也不會造成額外影響。工具可以列出 inode,但在傾印 inode 時就會失敗,因為該 inode 原本儲存的區塊對映已被清除,dump 沒有可追蹤的資訊。

有 2 個工具會嘗試讀取 journal 來繞過這項限制。journal 是固定大小的循環區域,ext4 用它在系統當機後維持中繼資料一致性。它可能仍保留刪除前的 inode 舊副本。extundeleteext4magic 都會搜尋 journal。先確認目前使用的大小:

sudo dumpe2fs -h /dev/vdb1 | grep -i journal

journal 只儲存中繼資料,而且容量很小,因此一般寫入活動會很快覆寫其中的內容。在執行中的伺服器上,刪除前 inode 仍存在的時間通常只有幾分鐘。這 2 個工具都沒有持續維護,也不是每個發行版都提供套件。請將它們視為成功機率很低的選項,並在未掛載的裝置或磁碟映像檔上執行。即使沒有找到任何內容,也不代表操作有問題。

如果 lsblk -f 回報 xfs,情況也不會比較好,因為 XFS 同樣沒有受支援的 undelete 工具。以下選項的順序不會改變。

執行中的程序是否仍開啟該檔案?

這是本頁中成功機率最高的復原方式,也是你不應重新啟動使用該檔案之服務的原因。

只有在兩個計數都歸零時,檔案才算真正消失:指向其 inode 的目錄項目數,以及開啟的檔案描述元數。rm 會將第一個計數降為零。如果程序仍持有該檔案的開啟描述元,第二個計數就不會是零,因此 inode 及其資料區塊仍會配置中,資料也仍可讀取。

找出連結計數已降為零的開啟檔案:

sudo lsof +L1

+L1 代表列出連結計數低於 1 的開啟檔案。每個結果會顯示程序、檔案描述元編號、NLINK0),以及以 (deleted) 結尾的路徑。取得 PID 與描述元編號後,將它們傳給 /proc

sudo ls -l /proc/1234/fd

項目看起來會像 3 -> /var/log/app/events.log (deleted)。該連結仍可存取資料。將資料複製到不同的檔案系統:

sudo cp /proc/1234/fd/3 /mnt/rescue/events.log

使用 cp,不要使用 mv。開啟 /proc/1234/fd/3 會在 offset zero 位置,針對相同的 inode 建立新的 handle,因此可取得完整檔案,而不是只取得寫入程序目前位置之後的部分。

有兩項限制需要注意。已刪除的目錄樹無法透過此方式復原,因為只有程序曾開啟的個別檔案仍會被保留。資料庫引擎正在寫入時複製的資料庫檔案,只是 crash-consistent copy,因此應規劃對該檔案執行引擎本身的復原程序,不要將其視為乾淨的資料庫副本。lsofmem 取代描述元編號所顯示的項目,代表這些檔案是 memory mapped;這類項目沒有可供複製的 /proc/<pid>/fd

btrfs、ZFS 或 LVM 上有快照嗎?

如果檔案系統支援快照,刪除的檔案仍會未經變更地保留在快照中。這只有在刪除前已存在快照時才有用。現在建立的快照無法追溯到過去。

btrfs 會將快照保留為子磁碟區:

sudo btrfs subvolume list /

瀏覽快照,並使用 cp -a 複製出需要的路徑。優先複製個別路徑,不要回復整個子磁碟區,因為回復也會捨棄建立快照後寫入的所有內容。

ZFS 會將每個快照公開為唯讀目錄:

zfs list -t snapshot
ls /tank/data/.zfs/snapshot/

.zfs 目錄會隱藏起來,因此不會出現在資料集根目錄的普通 ls 結果中,但可以依名稱進入該目錄。從中複製出檔案。zfs rollback 會將整個資料集回復到指定的快照,並刪除該快照之後建立的所有快照,因此應將它保留為最後手段。

LVM 快照是具有固定大小的寫入時複製磁碟區:

sudo lvs
sudo mount -o ro /dev/vg0/data-snap /mnt/snap

以唯讀方式掛載後複製出檔案。在信任快照前,先檢查 lvs,因為 LVM 快照填滿配置的空間後,會被 kernel 判定為無效;一旦發生這種情況,其中的內容就會遺失。

快照不是備份。快照與原始資料位於同一個磁碟或同一個 pool,因此會受到原始資料所遭遇的所有故障影響。快照非常適合復原兩分鐘前的誤操作,這正是此處需要的功能。

使用 PhotoRec 進行檔案雕刻,針對映像檔而非線上磁碟

如果上述方法都不適用,剩下的選項就是檔案雕刻:掃描原始裝置,尋找標示已知檔案類型開頭的位元組模式,再將後續內容寫出。檔案雕刻只會讀取檔案資料。檔名、目錄結構、時間戳記與擁有權都屬於檔案系統中繼資料,而 rm 破壞的正是這些中繼資料,因此無法復原。復原出的檔案會以 f0384512.jpg 命名,放在編號輸出目錄中,之後需要手動整理。

有兩項規則會決定這項方法是否有效。

第一,在讓其他工具存取裝置前,先建立裝置映像。在 Debian 和 Ubuntu 上,套件名稱是 gddrescue,安裝的 binary 是 ddrescue

sudo apt update && sudo apt install -y gddrescue testdisk
sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map

/mnt/rescue 必須位於不同裝置上,而且可用空間至少要與該分割區的容量相同。lsblk -b 會以 bytes 顯示精確大小。map 檔案可讓中斷的複製作業繼續執行,無須重新開始。建立映像後,稍後可以使用其他工具,針對完全相同的 bytes 再嘗試一次;如果第一個工具已覆寫磁碟,就無法這樣做。

第二,將復原工具指向映像檔。

sudo photorec /d /mnt/rescue/recup /mnt/rescue/vdb1.img

photorec 會開啟文字選單。依序選擇分割區、檔案系統類型、要搜尋的檔案簽章,以及目的地目錄。開始前,先將簽章清單縮小到實際遺失的檔案類型,因為預設清單會搜尋所有類型,最後產生數萬個片段供你篩選。

同一套件中的 testdisk 也具備自己的 undelete 功能,但只支援 FAT、exFAT、NTFS 和 ext2。在 ext4 上,這代表 photorec

請預期分散配置的檔案可能會復原失敗。檔案雕刻假設檔案的 blocks 連續,因此配置器分散到磁碟不同位置的檔案,可能會被錯誤重新組合,或完全找不到。媒體檔案通常能獲得不錯的雕刻結果,因為它們具有明確的 headers。純文字、設定檔與原始碼的雕刻結果通常較差,因為沒有任何 byte signature 能標示 shell script 的開頭。

多出的空格:錯誤的路徑如何遭到刪除

幾乎每次 rm -rf 意外都源自 shell 問題。rm 會接收路徑清單,逐一刪除每個路徑。它不會知道你的原意。

最典型的情況是多出一個空格:

rm -rf /home/deploy/app /old
rm -rf /home/deploy/app/old

第一行包含 2 個引數。它會先刪除應用程式,再刪除 /old。如果 /old 不存在,rm 完全不會輸出任何內容,因為 -f 會抑制找不到檔案的錯誤。沒有輸出不代表操作已確認安全。

第二種情況是未加引號的變數包含空格:

dir="/srv/my app"
rm -rf $dir

shell 會依空白字元分割該值,因此 rm 會將 /srv/myapp 視為 2 個不同的路徑。寫成 rm -rf "$dir" 時,它才是一個路徑。

第三種情況是變數為空,通常表示原本應填入該變數的命令執行失敗:

rm -rf "$TARGET"/*

TARGET 未設定時,這會展開為 rm -rf /*。GNU rm 拒絕這種裸形式:rm -rf / 會輸出 rm: it is dangerous to operate recursively on '/',然後停止。萬用字元形式沒有這項保護,因為 shell 會在 rm 執行前,先將 /* 替換為實際存在的頂層路徑清單,而 / 不在其中,因此防護條件不會觸發。

防止下一次意外的習慣

  • 所有用作路徑的變數都加上引號。每次都寫 "$dir",包括測試與迴圈內。
  • 空值時立即失敗。只要 TARGET 未設定或為空,rm -rf "${TARGET:?TARGET is not set}"/* 就會在 rm 開始前,讓 shell 顯示你的訊息並停止。任何會刪除內容的腳本,都應在頂端加入 set -euo pipefail
  • 加入 --one-file-system。它會告訴 rm 略過與指定引數位於不同檔案系統的目錄,避免遞迴刪除進入掛載的備份磁碟區或 bind mount。
  • 不要以 root 身分刪除。服務帳號只能刪除自己擁有的內容,這正是讓每項服務以自己的非特權使用者執行的理由。如果不確定某個帳號能存取哪些內容,讀取 ls 清單中的權限位元即可用一個命令確認。
  • 執行前先列印清單。在腳本中建立路徑、執行 printf '%s\n'、閱讀輸出,然後在第二次執行時才刪除。
  • 隨手備妥 trash 命令。sudo apt install trash-cli 會提供 trash-puttrash-listtrash-restoretrash-empty。刪除的檔案會移至 ~/.local/share/Trash,而 trash-empty 30 會清除超過 30 天的內容。

rm 設為 trash-put 的別名,看似是下一個合理步驟,實際上卻是陷阱。這個別名會養成反射式操作習慣;下一台沒有該別名的伺服器就會失效。而且別名不會套用到腳本中,偏偏代價最高的錯誤通常發生在腳本裡。請刻意輸入 trash-put

每次都能奏效的唯一復原方式

以上做法都只是機會。備份不是機會。

備份要真正可靠,必須符合兩項條件。它會按照排程執行,不必靠你記得手動執行;而且你至少曾經從中完成一次還原。從未有人還原過的 repository 只是一種信念,因為讓備份失效的問題(例如 include 清單中的路徑錯誤,或沒有人記下 repository 密碼)只會在真正需要時才暴露。

使用 restic 還原只需要兩個命令。

restic -r /srv/restic-repo snapshots
restic -r /srv/restic-repo restore latest --target /mnt/rescue --include /srv/appdata

請還原到空目錄,不要直接覆寫執行中的路徑。這樣可以先比較兩者,再將任何內容移入正確位置。在 VPS 上設定 restic 備份涵蓋 repository 設定,以及執行備份的 systemd timer。

使用 Borg:

borg list /srv/borg-repo
borg extract /srv/borg-repo::daily-2026-08-08 srv/appdata

Borg archive 內的路徑會儲存成不含開頭斜線的形式,因此 srv/appdata 會比對成功,而 /srv/appdata 不會比對到任何內容。borg extract 會寫入目前的工作目錄,因此請先將 cd 寫入暫存目錄。

如果你尚未在兩者之間做出選擇,restic 與 Borg 的比較涵蓋 deduplication 與 append-only repository。後者能防止遭入侵的伺服器刪除自己的備份歷程。使用任一工具都可以。錯誤的選擇是兩者都沒有執行。

在新伺服器上設定備份最省事,因為此時伺服器上還沒有任何值得遺失的資料。新 VPS 上線後的前 10 分鐘就是處理這項工作的時機,也應同時完成 SSH 與防火牆設定。

接著在行事曆中建立週期性提醒:每個月從 repository 還原一個目錄到 /tmp,並讀取其中的檔案。這個單一習慣比本頁上的所有工具都更有價值。

FAQ

我可以復原 ext4 上刪除的檔案嗎?

通常不行。檔案的最後一個連結消失後,ext4 會從 inode 清除 extent tree,因此磁碟上不會再記錄資料原本所在的位置。extundeleteext4magic 會搜尋 ext4 journal,尋找該 inode 較早的副本;只有在刪除發生於幾分鐘前,且檔案系統自此一直沒有活動時,這種方法才可能有幫助。這兩個專案目前都未積極維護。請對未掛載的裝置或磁碟映像執行其中一個工具,絕不要對已掛載的檔案系統執行。開始前,先使用 sudo dumpe2fs -h /dev/vdb1 | grep -i journal 確認目前操作的對象。

服務仍開啟已刪除的檔案。我可以取回它嗎?

可以,這是最理想的情況。只要程序仍持有檔案,該檔案的 inode 和資料區塊就會維持配置狀態,因此仍可讀取資料。不要重新啟動服務,因為關閉最後一個 descriptor 就會完成刪除。執行 sudo lsof +L1,列出 link count 為 0 的開啟檔案,記下 PID 和 file descriptor 編號,接著使用 /proc 搭配 sudo cp /proc/1234/fd/3 /mnt/rescue/events.log 複製資料。請將副本寫入不同的檔案系統。若 mem 顯示的不是 descriptor 編號,表示該檔案是 memory mapped,沒有可供複製的 /proc/<pid>/fd 路徑。

為什麼應該先建立磁碟映像,而不是直接在磁碟上執行復原工具?

因為所有工具都必須將輸出寫入某處,而寫入正在復原的檔案系統時,資料可能會落在仍包含檔案資料的可用區塊上。請先使用 sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map 將分割區複製到其他裝置,再讓 photorec 指向映像檔。建立映像後,之後也能使用完全相同的位元組,讓第二個工具再次嘗試復原;一旦原始磁碟遭到覆寫,就無法做到這點。

rm -rf / 仍會摧毀 Linux 系統嗎?

單獨執行該命令不會。GNU rm 會拒絕執行,並顯示 rm: it is dangerous to operate recursively on '/'。真正危險的是透過其他方式產生的命令形式。當 TARGET 未設定時,rm -rf "$TARGET"/* 會展開為 rm -rf /*,接著 shell 會將實際存在的頂層目錄清單交給 rm;其中沒有 /,因此防護機制不會觸發。請改寫為 "${TARGET:?TARGET is not set}",如此 shell 會在 rm 執行前停止。

檔案系統 snapshot 算是備份嗎?

不算。btrfs 或 ZFS snapshot 與受保護的資料位於同一個 pool,因此磁碟故障或 pool 損毀時,兩者會同時遺失。LVM snapshot 還有固定大小的問題:一旦空間用盡,kernel 就會使 snapshot 失效,其中的內容也會消失。Snapshot 很適合復原兩分鐘前誤刪的檔案。除此之外,請在獨立硬體上維護 repository。

#linux#rm#data-recovery#backups#ext4