前陣子 YouTube 首頁滑到一個影片,是知名電腦教學頻道「Papaya 電腦教室」遇到可怕意外,使用了二十年的雲端硬碟服務 OneDrive,忽然間檔案全都不見,用盡各種方法但找不回來。跟微軟線上客服講半天才察覺是跟 AI 對話,但想找真人聯繫又苦無門路。不得已拍了影片詳述過程讓大家引以為戒,順便跟粉絲求救是否認識在微軟上班的聯絡窗口。
關於這件事的後續,第六感告訴我檔案應該有找回來,因為現在已經看不到當初的影片,可惜了 Papaya 還為此做一首歌,旋律還不錯。後來在這個討論串「PAPAYA在OneDrive的資料被清空」,有網友提供了不少截圖,算是證實了我的猜想。
即便 OneDrive 能救回檔案,也不代表人人都有此境遇,畢竟我也遇過類似情境,在不確定網路上的帳號、資產能否救回之前,每一天都是巨大的煎熬。除非有門路、能拍影片、能寫歌,否則網路上的國際大公司,不是隨便一個人可以找到聯絡窗口對話。
所以這件事是個相當大的警醒,雖然我不使用 OneDrive,但會使用更普遍的 Dropbox、Google Drive 等雲端硬碟。過去因為信任世界級公司,認為對檔案安全會有很好的控管,從沒想過定期備份雲端檔案(通常是多年後換配備時才會順便複製一份),不過現在開始決定先做好萬全準備,免得累積多年的重要檔案、資訊毀於一旦。
長期手動定時備份檔案,是一件相當繁複的事,做久了容易倦怠,身為工程師自然是想辦法把整個流程程式化,讓電腦自動定期完成這件事。本篇會分享如何用 PowerShell 寫指令,配合 Windows 工作排程自動執行備份雲端硬碟檔案,以 Dropbox 的操作進行舉例,並對備份檔案進行版本管理,除了確保安全性,也可不佔用過多硬碟空間。
雖然只是個小程式,不過仍有這些不錯的設計:
- 防錯、防呆,錯誤與操作都有提示
- 動態顯示複製檔案數、進行百分比
- 顯示備份狀態、版本控制狀態
- 列出備份失敗檔案
一、缺乏備份的風險
首先說明如果像我之前一樣,沒有定期備份雲端硬碟檔案,會面臨什麼風險: 1. 內部失誤- 不小心自己修改內容出錯
- 自己的操作失誤、誤刪檔案
- 磁碟壞軌、硬體問題導致檔案錯誤
二、備份原則與版本管理方法
1. 資料備份原則 對於一般使用者(非企業)而言,採用「3-2-1 備份策略」便足夠:- 保留 "三" 份資料
- 使用 "兩" 種裝置儲存
- 異地保存 "一" 份資料
- 時間較近的週期,保留越多版本數
- 時間較遠的週期,保留越多版本數
- 公司(企業)的資料比較重要,需要頻繁備份,例如:
- 每天備份,保留 14 個版本
- 每週備份,保留 4 個版本
- 每月備份,保留 3 個版本
- 每月備份,保留約 3~6 個版本,時間更早的月備份則刪除
- 除了月備份之外,時間更早的版本,每 3 個月(也就是 1 季)保留一個版本,最多保留 2~4 個版本,時間更早的季備份則刪除
三、PowerShell 腳本
1. 腳本概念 PowerShell 腳本執行的邏輯大致是這樣:- 備份數量設定最多保留 X 個月、Y 季
- 每月自動複製 Dropbox 資料夾內容,並將資料夾用「年-月」參數來命名
- 每次複製完,檢查保留月份數是否超過 X,超過的話刪除較早的資料夾
- 檢查保留季份數是否超過 Y,超過的話刪除較早的資料夾
- 備份過程執行階段及出現錯誤,顯示提醒訊息
# ===========================================
# 功能:自動月份備份與版本控制
# 出處:WFU BLOG
# 網址:https://wfublog.com/2026/03/auto-backup-schedule-dropbox.html
# ===========================================
# ========== 參數設定 ==========
$sourcePath = "D:\Dropbox" # 來源資料夾路徑,可改為自己 Dropbox 路徑
$targetPath = "E:\Dropbox_Backup" # 目標資料夾路徑
$maxMonths = 4 # 最多保留月份數
$maxQuarters = 3 # 最多保留季數
# ========== 執行提示 ==========
Clear-Host
Write-Host "========================================" -ForegroundColor Cyan
Write-Host " 每月資料夾備份與版本控制" -ForegroundColor Cyan
Write-Host " 來源:$sourcePath" -ForegroundColor Cyan
Write-Host " 目標:$targetPath" -ForegroundColor Cyan
Write-Host " 保留月份數:$maxMonths 保留季數:$maxQuarters" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# ========== 來源資料夾檢查 ==========
if (-not (Test-Path $sourcePath)) {
Write-Host "來源資料夾不存在:$sourcePath" -ForegroundColor Red
exit 1
}
$sourceFiles = Get-ChildItem -Path $sourcePath -Recurse -File
if (-not $sourceFiles) {
Write-Host "來源資料夾內沒有任何檔案:$sourcePath" -ForegroundColor Yellow
$response = Read-Host "是否仍要繼續?(y/n,預設為 n)"
if ($response -ne "y") {
Write-Host "已取消備份操作。" -ForegroundColor Gray
exit 0
}
}
# ========== 目標資料夾檢查 ==========
if (-not (Test-Path $targetPath)) {
Write-Host "目標資料夾不存在:$targetPath" -ForegroundColor Yellow
$response = Read-Host "是否自動建立目標資料夾?(y/n,預設為 n)"
if ($response -eq "y") {
try {
New-Item -ItemType Directory -Path $targetPath | Out-Null
Write-Host "已建立目標資料夾:$targetPath" -ForegroundColor Cyan
} catch {
Write-Host "建立目標資料夾時發生錯誤:$($_.Exception.Message)" -ForegroundColor Red
exit 1
}
} else {
Write-Host "已取消備份操作。" -ForegroundColor Gray
exit 0
}
}
# ========== 建立新備份資料夾並複製檔案 ==========
$currentMonth = Get-Date -Format "yyyy-MM"
$newFolder = Join-Path $targetPath $currentMonth
try {
if (Test-Path $newFolder) {
Write-Host "目標資料夾已存在:$newFolder" -ForegroundColor Yellow
$response = Read-Host "是否覆蓋?(y/n,預設為 n)"
if ($response -eq "y") {
Remove-Item -Path "$newFolder\*" -Recurse -Force
Write-Host "已清除原有內容,準備重新複製。" -ForegroundColor Cyan
} else {
Write-Host "已取消覆蓋,略過複製步驟。" -ForegroundColor Gray
}
} else {
New-Item -ItemType Directory -Path $newFolder | Out-Null
$response = $null
}
if ((Test-Path $newFolder) -and ($response -eq "y" -or $null -eq $response)) {
Write-Host "正在讀取來源檔案與資料夾清單,請稍候..." -ForegroundColor Cyan
# 先複製完整資料夾結構(含空資料夾)
$allDirs = Get-ChildItem -Path $sourcePath -Recurse -Directory
foreach ($dir in $allDirs) {
$relativeDir = $dir.FullName.Substring($sourcePath.Length)
$destDir = Join-Path $newFolder $relativeDir
if (-not (Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir | Out-Null
}
}
# 逐一複製檔案並顯示進度
$allFiles = Get-ChildItem -Path $sourcePath -Recurse -File
$totalCount = $allFiles.Count
$copiedCount = 0
$failedFiles = @()
foreach ($file in $allFiles) {
$relativePath = $file.FullName.Substring($sourcePath.Length)
$destFile = Join-Path $newFolder $relativePath
try {
Copy-Item -Path $file.FullName -Destination $destFile -Force -ErrorAction SilentlyContinue
} catch {
$failedFiles += $file.FullName
}
$copiedCount++
$percent = [math]::Round(($copiedCount / $totalCount) * 100)
Write-Progress -Activity "複製檔案中..." -Status "$copiedCount / $totalCount 檔案 ($percent%)" -PercentComplete $percent
}
Write-Progress -Activity "複製檔案中..." -Completed
if ($failedFiles.Count -gt 0) {
Write-Host "複製完成,但以下 $($failedFiles.Count) 個檔案複製失敗:" -ForegroundColor Yellow
$failedFiles | ForEach-Object { Write-Host " $_" -ForegroundColor Yellow }
} else {
Write-Host "已成功複製所有檔案至:$newFolder" -ForegroundColor Green
}
}
} catch {
Write-Host "複製檔案時發生錯誤:$($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# ========== 取得所有備份資料夾,依時間從新到舊排序 ==========
try {
$allFolders = Get-ChildItem -Path $targetPath -Directory |
Where-Object { $_.Name -match '^\d{4}-\d{2}$' } |
Sort-Object { [datetime]::ParseExact($_.Name, "yyyy-MM", $null) } -Descending
} catch {
Write-Host "讀取目標資料夾時發生錯誤:$($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# ========== 版本控制 ==========
if ($allFolders.Count -le $maxMonths) {
Write-Host "備份數量未超過保留月份數($maxMonths),略過版本控制。" -ForegroundColor Gray
} else {
$keepZone = $allFolders | Select-Object -First $maxMonths
$quarterZone = $allFolders | Select-Object -Skip $maxMonths
$baseMonth = [datetime]::ParseExact($keepZone[-1].Name, "yyyy-MM", $null)
$keepQuarterFolders = @()
for ($i = 0; $i -lt $maxQuarters; $i++) {
$quarterEnd = $baseMonth.AddMonths(-($i * 3) - 1)
$quarterStart = $baseMonth.AddMonths(-($i * 3) - 3)
$thisQuarter = $quarterZone | Where-Object {
$m = [datetime]::ParseExact($_.Name, "yyyy-MM", $null)
$m -ge $quarterStart -and $m -le $quarterEnd
} | Sort-Object { [datetime]::ParseExact($_.Name, "yyyy-MM", $null) }
if ($thisQuarter) {
$keepQuarterFolders += $thisQuarter | Select-Object -First 1
}
}
foreach ($folder in $quarterZone) {
if ($keepQuarterFolders.Name -notcontains $folder.Name) {
try {
Remove-Item -Path $folder.FullName -Recurse -Force
Write-Host "已刪除過期備份:$($folder.Name)" -ForegroundColor DarkGray
} catch {
Write-Host "刪除資料夾時發生錯誤:$($folder.Name),原因:$($_.Exception.Message)" -ForegroundColor Red
}
} else {
Write-Host "保留季度備份:$($folder.Name)" -ForegroundColor Cyan
}
}
}
Write-Host "備份與版本控制完成。" -ForegroundColor Green
- 紅字參數請根據註解,修改備份來源資料夾路徑、目標資料夾路徑
- 綠字參數請根據註解,修改最多保留的月數、季數
- 請將以上內容存成
.ps1 副檔名,例如 backup.ps1 - 儲存時編輯軟體記得選取「UTF-8 (with) BOM」這種編碼格式,執行時視窗的提示文字才能正確顯示中文
- 複製過程若出現錯誤、需要確認的情況,會進行提示
- 動態顯示複製進度:複製檔案數、進行百分比
- 顯示備份狀態、版本控制狀態
- 備份失敗的檔案會列出
四、Windows 設定工作排程器
按 Win+R 輸入以下並執行:taskschd.msc
按以下步驟新增排程:
- 點右側「建立工作」
- 名稱:例如「Dropbox 定期備份」
- 選擇「只有使用者登入時才執行」、勾選「以最高權限執行」
- 觸發程序:可設定「每月」指定日期
- 例如「月份」勾選每個月,「天」選擇第 25 天,並設定時間
- 動作:選擇「啟動程式」
- 程式或指令碼:填入 cmd
- 新增引數:填入 /c start powershell.exe -ExecutionPolicy Bypass -NoExit -File "e:\backup.ps1" → 其中 "e:\backup.ps1" 請換成你的 ps1 檔路徑
- 勾選「在錯過排定的啟動後盡快執行工作」
- 勾選「如果工作失敗,每隔以下時間重新啟動」,並設定希望的間隔時間
- 勾選「如果工作執行時間大於以下值即停止」,選擇你希望的時間
五、備份結果與技巧
1. 備份結果範例 想要先知道備份效果的話,以下舉例設定「最多保留 2 月、2 季」的情況下,從 2025 年 1 月開始,連續備份 12 次後的結果,會留下哪些版本:- 1月:2025-01
- 2月:2025-02, 2025-01
- 3月:2025-03, 2025-02, 2025-01
- 4月:2025-04, 2025-03, 2025-01
- 5月:2025-05, 2025-04, 2025-01
- 6月:2025-06, 2025-05, 2025-04, 2025-01
- 7月:2025-07, 2025-06, 2025-04, 2025-01
- 8月:2025-08, 2025-07, 2025-04, 2025-01
- 9月:2025-09, 2025-08, 2025-07, 2025-04
- 10月:2025-10, 2025-09, 2025-07, 2025-04
- 11月:2025-11, 2025-10, 2025-07, 2025-04
- 12月:2025-12, 2025-11, 2025-10, 2025-07
- 最多 6 月 + 最多 2 季:總共 8 個版本
- 最多 4 月 + 最多 3 季:總共 7 個版本
- 最多 3 月 + 最多 3 季
- 一樣可達到約 1 年的跨度
- 硬碟只須保留 6 個版本,較節省空間
- 備份來源(Dropbox)資料夾最好不要跟目標資料夾同個裝置
- 例如 Dropbox 如果放在系統 SSD 碟,目標資料夾可選擇另一個 SSD 碟、或 HDD 碟。而我因為電腦有插一個外接硬碟,所以備份的目標資料夾選擇外接硬碟。
- 為了防止外接硬碟發生意外,每年我也會定期將外接硬碟的內容,手動複製(同步)到另一顆外接硬碟,這樣就非常安全了
更多「資訊安全」相關文章:
沒有留言:
張貼留言注意事項:
◎ 勾選「通知我」可收到後續回覆的mail!
◎ 請在相關文章留言,與文章無關的主題可至「Blogger 社團」提問。
◎ 請避免使用 Safari 瀏覽器,否則無法登入 Google 帳號留言(只能匿名留言)!
◎ 提問若無法提供足夠的資訊供判斷,可能會被無視。建議先參考這篇「Blogger 提問技巧及注意事項」。
◎ CSS 相關問題非免費諮詢,建議使用「Chrome 開發人員工具」尋找答案。
◎ 手機版相關問題請參考「Blogger 行動版範本的特質」→「三、行動版範本不一定能執行網頁版工具」;或參考「Blogger 行動版範本修改技巧 」,或本站 Blogger 行動版標籤相關文章。
◎ 非官方範本問題、或貴站為商業網站,請參考「Blogger 免費諮詢 + 付費諮詢」
◎ 若是使用官方 RWD 範本,請參考「Blogger 推出全新自適應 RWD 官方範本及佈景主題」→ 不建議對範本進行修改!
◎ 若留言要輸入語法,"<"、">"這兩個符號請用其他符號代替,否則語法會消失!
◎ 為了過濾垃圾留言,所有留言不會即時發佈,請稍待片刻。
◎ 本站「已關閉自刪留言功能」。