⚡ 站長快讀:核心重點
- 文章屬性:教學實戰
- 適用系統:Windows 10 / 11(PowerShell 5.1 與 7 皆可)
- 難易度:⭐⭐(看得懂 PowerShell 即可)
- 核心結論:PowerShell FileSystemWatcher 資料夾監控走事件觸發,檔案一異動就立刻跑腳本,與工作排程器「時間到才去看」是兩套機制。
- 適用對象:想讓下載、掃描、報表匯出資料夾在檔案落地當下就自動處理的人
📌 快速答案
一句話答案:PowerShell FileSystemWatcher 資料夾監控,是用
Register-ObjectEvent把 Created、Changed、Deleted、Renamed 四個事件綁上腳本區塊,資料夾內檔案一有異動就即時執行,不必等排程時間到。
🧰 開始前的準備
- 系統需求:Windows 10 或 Windows 11;
System.IO.FileSystemWatcher屬於 .NET 內建類別,不需要另外安裝任何東西 - 權限需求:一般使用者即可(監控的資料夾你有讀取權限就行)
- 需要工具:Windows PowerShell 5.1(系統內建)或 PowerShell 7
- 預計耗時:照著貼完三段指令就能看到第一筆事件紀錄
- 難度門檻:知道怎麼開 PowerShell、看得懂變數與大括號即可
⚠️ 停止條件:下列情形請先停下再繼續——①要監控的是公司或學校管控的共用磁碟且你沒有 IT 授權;②動作腳本裡打算做刪除、移動或覆寫,而來源資料夾沒有備份;③你不確定該資料夾每分鐘會產生幾筆變更(數量級不明時先只寫 log 觀察,不要直接接上會改檔案的動作)。
🔍 為什麼你需要這個?
排程備份很好用,但它有一個先天限制:你設每小時跑一次,最壞情況就是檔案落地後要等 59 分鐘才被處理。掃描器丟進來的 PDF、下載完的報表、同事共用資料夾裡新增的 CSV,這些情境要的是「一落地就處理」,不是「等下一輪」。
FileSystemWatcher 就是為此存在的。它不輪詢、不掃目錄,而是掛在作業系統的檔案變更通知上,由系統主動推事件給你。本篇要把三件網路教學很少講清楚的事講完:
- 它和工作排程器不是替代關係,是互補關係——判斷準則在下面的對照表。
- 官方文件白紙黑字寫著「一次操作可能觸發多個事件」——所以你的腳本被叫兩三次不是 bug,是規格,要自己處理。
- 它有一個 8 KB 的內部緩衝區,滿了會靜默漏事件——這是最容易讓人以為「腳本壞了」的地方。
🛠️ 實戰步驟
步驟一:建立監看器並先把範圍縮到最小
先講結論:監控範圍開得愈大,漏事件的機率愈高。所以第一步不是急著綁事件,而是把 Filter、IncludeSubdirectories、NotifyFilter 三個閥門先關小。
# 建立監看器,只看 D:\Inbox 這一層的 .csv 檔
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = 'D:\Inbox'
$watcher.Filter = '*.csv'
$watcher.IncludeSubdirectories = $falseFilter 設空字串或 *.* 代表全部檔案;設 *.csv 就只看 CSV。NotifyFilter 則是決定「哪一種變更才算數」,官方預設值是 LastWrite、FileName、DirectoryName 三者的位元 OR 組合。想再收斂就自己指定:
# 只在「檔名變動」與「最後寫入時間變動」時發事件
$watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName -bor
[System.IO.NotifyFilters]::LastWrite
$watcher.EnableRaisingEvents = $true💡 為什麼要這樣做? 微軟官方在
FileSystemWatcher類別總覽的說明裡寫得很明確:用NotifyFilter與IncludeSubdirectories濾掉不需要的變更通知,就是避免內部緩衝區溢位的正規手段。緩衝區來自無法置換到磁碟的非分頁記憶體(non-paged memory),加大很貴,先過濾比較划算。
步驟二:用 Register-ObjectEvent 綁定四個事件
FileSystemWatcher 對外公開 Created、Changed、Deleted、Renamed 四個變更事件,外加一個 Error 事件。PowerShell 這端用 Register-ObjectEvent 訂閱:
# 事件發生時要做的事:先只寫一行 log,確認訊號有進來
$action = {
$e = $Event.SourceEventArgs
$now = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
"$now`t$($e.ChangeType)`t$($e.FullPath)" |
Add-Content -Path 'D:\Inbox\watch.log'
}# 四個事件各註冊一次,SourceIdentifier 取好記的名字方便之後取消
foreach ($n in 'Created','Changed','Deleted','Renamed') {
Register-ObjectEvent -InputObject $watcher -EventName $n `
-SourceIdentifier "FSW.$n" -Action $action | Out-Null
}官方文件載明:-Action 的腳本區塊裡可以用 $Event、$EventSubscriber、$Sender、$EventArgs、$args 這幾個自動變數;而且一旦指定了 -Action,事件就不會再進事件佇列,改由這段動作直接回應。-SourceIdentifier 不填的話,PowerShell 會自動指派一個 GUID——那就很難取消了,所以務必自己命名。
Renamed 事件另外多一個 OldFullPath 屬性,可以拿到改名前的完整路徑。順帶把 Error 也綁上,溢位時才有跡可循:
Register-ObjectEvent -InputObject $watcher -EventName Error `
-SourceIdentifier 'FSW.Error' -Action {
$Event.SourceEventArgs.GetException().Message |
Add-Content -Path 'D:\Inbox\watch-error.log'
} | Out-Null步驟三:驗證結果
Get-EventSubscriber應該看到 FSW.Created 等五筆訂閱。接著手動丟一個檔案進去試:
New-Item -Path 'D:\Inbox\test.csv' -ItemType File
Start-Sleep -Seconds 1
Get-Content -Path 'D:\Inbox\watch.log'log 裡出現 Created 那一行,就代表整條鏈路通了。如果你看到不只一行,先別急著改程式——下一節就是在講這件事。
🔁 即時監控 vs 定時排程:什麼時候該用哪一個
先給結論:要「立刻反應」用 FileSystemWatcher,要「保證一定會跑到」用工作排程器;兩者疊在一起用,才是穩的做法。
| 比較項目 | FileSystemWatcher | 工作排程器 |
|---|---|---|
| 觸發方式 | 檔案異動事件推播 | 時間到了才執行 |
| 反應延遲 | 幾乎即時 | 最長等一個排程間隔 |
| 沒開機/沒登入時 | 不會執行,事件也不會補 | 可設定開機後補跑 |
| 漏掉的風險 | 緩衝區滿了會漏 | 幾乎不會漏 |
實務上最穩的組合是:平常靠 FileSystemWatcher 即時處理,再用工作排程器每天排一次「掃全資料夾補處理」當保險絲。如果你要的其實只是定時備份,那不需要用到本篇的東西,直接看 PowerShell 定時備份那篇更省事。
🧯 三個一定會踩到的雷
雷一:一個動作觸發好幾次
這是官方規格,不是你寫錯。微軟文件寫得很直白:常見的檔案系統操作可能觸發多個事件——例如把檔案從一個目錄搬到另一個目錄,可能同時引發數個 OnChanged 與若干 OnCreated、OnDeleted;防毒軟體之類的程式也會製造額外的檔案系統事件被監看器偵測到。
三個處理方向:①NotifyFilter 只留真正需要的旗標;②只綁真正需要的事件(多數情境其實只要 Created);③在動作裡自己做去抖,同一個路徑在極短時間內重複進來就跳過(去抖屬社群通行做法,官方文件並未提供內建去抖機制)。
另外一個常被誤判成 bug 的情境:Created 事件是在檔案「被建立」時就發出,大檔案這時候往往還沒寫完。實務上會加一段等待,確認檔案不再被鎖住再動它(這是社群通行做法,非官方規格):
# 最多等 3 秒,確認檔案已經可以獨佔開啟
for ($i = 0; $i -lt 10; $i++) {
try {
$fs = [IO.File]::Open($path, 'Open', 'Read', 'None')
$fs.Close(); break
} catch { Start-Sleep -Milliseconds 300 }
}雷二:PowerShell 視窗一關,監控就沒了
官方在 Register-ObjectEvent 的「備註」寫得很清楚:事件、事件訂閱與事件佇列只存在於目前的工作階段;關掉這個工作階段,事件佇列會被丟棄、訂閱也會被取消。
所以它不會自己變成常駐服務。要長期執行,標準做法是把整段腳本存成 .ps1,結尾加一個讓工作階段不結束的迴圈,再用工作排程器設定「使用者登入時」或「電腦啟動時」啟動它:
# 放在 .ps1 結尾,讓這個工作階段活著
while ($true) { Start-Sleep -Seconds 5 }雷三:緩衝區滿了會靜默漏事件
作業系統把變更通知寫進 FileSystemWatcher 建立的緩衝區,短時間內變更太多就會溢位。溢位的後果不是報錯中斷,而是元件失去對該目錄變更的追蹤,只給概括通知——你的腳本就只是「少跑了幾次」,很難察覺。
幾個官方數字值得記下來:InternalBufferSize 預設 8192 位元組(8 KB);可以設到 4 KB 以上、但不得超過 64 KB;設成小於 4096 會被直接丟棄並強制設回 4096;每個事件最多佔用 16 位元組(不含檔名)。監控網路上的目錄時,上限一樣是 64 KB。
官方給的三條避雷建議:加大 InternalBufferSize、避免監控檔名很長的檔案(長檔名很吃緩衝區)、事件處理程式碼盡量寫短。最後這條最重要——動作裡要做重活,請把事情丟給另一支腳本或佇列,不要卡在事件處理程序裡。
# 高流量資料夾可以把緩衝區拉大(單位:位元組,上限 65536)
$watcher.InternalBufferSize = 32768🔙 萬一翻車:回退步驟
監控接錯資料夾、或動作腳本行為不如預期時,依序做下面三件事就能回到乾淨狀態:
# 1. 取消所有訂閱
foreach ($n in 'Created','Changed','Deleted','Renamed','Error') {
Unregister-Event -SourceIdentifier "FSW.$n" -ErrorAction SilentlyContinue
}# 2. 停止並釋放監看器
$watcher.EnableRaisingEvents = $false
$watcher.Dispose()- 若你已經用工作排程器設了開機自動啟動,到「工作排程器」把那筆工作停用或刪除;最壞情況直接關掉該 PowerShell 視窗,訂閱會隨工作階段一起消失(見雷二)。
💡 總結:先只寫 log,再接動作
站長我的建議很簡單:任何 FileSystemWatcher 腳本,第一版都只寫 log,不要動檔案。先觀察一天,你會很清楚地看到同一個檔案被觸發幾次、有沒有防毒軟體在製造雜訊;這些資訊決定了你要不要去抖、要不要加大緩衝區、要不要縮 NotifyFilter。等 log 乾淨了,再把真正的動作接上去。
三個可驗證的錨點請記牢:InternalBufferSize 預設 8192 位元組、上限 64 KB;NotifyFilter 預設是 LastWrite、FileName、DirectoryName 的組合;事件訂閱只活在目前工作階段。本文所有規格皆依 Microsoft Learn 官方文件(2026-08-09 查證)撰寫,非站長實機實測數據。
不熟 PowerShell 語法的話,建議先補 PowerShell 入門教學再回來看本篇會順很多。
❓ 常見問題
Q:可以監控網路磁碟機或另一台電腦的資料夾嗎?
可以。官方明載 FileSystemWatcher 能監看本機、網路磁碟機或遠端電腦上的檔案;遠端電腦需要安裝所需平台。但監控網路目錄時 InternalBufferSize 上限一樣是 64 KB,而網路延遲會讓漏事件的機率變高,建議搭配定時掃描當保險。
Q:把一整個資料夾剪下貼上進監控目錄,為什麼只收到一個事件?
因為作業系統和 FileSystemWatcher 會把剪下貼上或移動,解讀成資料夾與其內容的「重新命名」。官方文件說明:這時只會回報該資料夾是新的,不會回報裡面的內容。要抓到內容,官方建議同時提供 OnChanged 與 OnRenamed 兩個處理程序,並在需要時遞迴展開。
Q:隱藏檔會被忽略嗎?光碟會觸發事件嗎?
隱藏檔不會被忽略,一樣會發事件。CD 與 DVD 則不會觸發事件,因為時間戳記與屬性不會改變。另外官方提醒:某些系統上檔名會以 8.3 短檔名格式回報,例如 LongFileName.LongExtension 可能被報成 LongFil~.Lon,寫比對邏輯時要留意。
Q:這個方法在 PowerShell 7 也適用嗎?
適用。FileSystemWatcher 是 .NET 類別、Register-ObjectEvent 是 Microsoft.PowerShell.Utility 模組的 cmdlet,兩者在 Windows PowerShell 5.1 與 PowerShell 7 都在。
🔗 延伸閱讀
- Windows 批次檔入門教學:5 個 .bat 實用範例直接抄
- AutoHotkey v2 入門教學:自訂快捷鍵+文字快速輸入一次上手
- 正規表示式 regex 入門:找檔案、抓資料、批次改文字一次學會
- Python 自動化第一課:批次改檔名、自動整理資料夾一次學會
📎 參考資料來源
📖 第一級|廠商官方:
- FileSystemWatcher Class (System.IO) — Microsoft Learn — 2026-08-09 查證
- FileSystemWatcher.InternalBufferSize Property — Microsoft Learn — 2026-08-09 查證
- FileSystemWatcher.NotifyFilter Property — Microsoft Learn — 2026-08-09 查證
- Register-ObjectEvent (Microsoft.PowerShell.Utility) — Microsoft Learn — 2026-08-09 查證
⚠️ 本文核心事實以第一級為準,全文為官方文件查證(E3),未含站長實機實測數據。
📅 本文查證戳記:2026-08-09 依 Microsoft Learn 官方文件撰寫,適用 Windows 10 / Windows 11 上的 Windows PowerShell 5.1 與 PowerShell 7。
若你在後續版本遇到步驟失效,歡迎在留言區回報,站長會更新文章。