檔案備份

0. 前言


各位是否遇過報告寫到一半,電腦突發惡疾必須送修的情況?雖然許多雲端服務可以幫助我們自動備份,但有時候,出於隱私考量或避免付費,我們更希望在本地磁碟保存。此時只要有個外接設備,就能夠自動達成這項目的了。就讓我們來寫份腳本進行定時工作吧!

1. 重點


定期本地備份可以確保資料不經他人保管,並且擁有高可用性救濟手段。
注意備份時不要建立在來源之下,這會導致每次備份進行 Cyclic Copy,東西倍數成長。

2. 內容


2.1. 為何備份?

勒索病毒

電腦受到勒索病毒,無法正常瀏覽檔案。只能到別台未受攻擊的機台倚靠備份還原。

保護心血

設施遇到天災、失誤甚至惡意攻擊,造成多日的心血直接清空。若有備份則能減少進度損失。

2.2. 腳本範例

首先準備一個新的資料夾,在裡面準備子資料夾與檔案來做測試。

理論上腳本會幫忙建立資料夾,這邊讓各位看見,我將備份存放的地方放在來源外面,這是為了避免重複複製到備份的內容。Cyclic Copy 這概念在許多資源操作都是慎重提醒的,所以筆者就沒做這部分的篩選。

此時筆者測試腳本,可以看到執行紀錄,並且發現新增一個備份。我們接著到內部檢查一下,是否跟上方截圖畫面中的內容一致,確認是否有缺漏。

因為檔名格式依靠 HH:MM,若要測試保存份數,在備份倉庫內複製貼上,超出數量也沒有關係。此時重新執行之後,應該只會剩下指定數量的資料,並會顯示刪除哪些。但本人偷懶,畢竟理論上就是一個週期,沒有多做 logging,各位可以嘗試在備份位置之下添加 log.txt,方便後續可以追蹤紀錄。

為了拆解的易懂且實用,腳本稍加繁瑣一些。而因為使用指定路徑的方式,不一定要放在檔案目錄,這也是為了避免腳本同時被複製。另外就要再度提到剛剛 Cyclic Copy 的概念,若我們將儲存路徑放在來源之下,此時倉庫裡的備份也再重新複製一輪,隨著次數增多,雪球將會越滾越大。

麻煩各位記得調整兩個路徑(L11, L13),也能夠自己修改檔名格式(L28, L32),還有要保留幾份的近期存檔(L16)。由於在檢查近期存檔的時候,筆者有過濾檔名格式,因此若有需要,能把上面的部分分離,建立一個額外的方法,透過不同的備份名稱,作為擁有保存的存檔。

BAT (Batchfile)
@echo off

@REM 使用 UTF-8 編碼(不然中文會呈現亂碼)
chcp 65001 >nul

@REM 啟用延遲展開
setlocal enabledelayedexpansion

@REM 設定變數
:: 來源位置
set "SRC=來源的絕對路徑"
:: 倉庫位置
set "DST=倉庫的絕對路徑(請不要在 SRC 之下,造成 Cyclic Copy)"

:: 保留份數
set maxBackups=7

:: 檔案計數
set count=0

:: 建立倉庫
if not exist "%DST%" mkdir "%DST%"

@REM 取得時間戳記
:: 取得本地時間
for /f "tokens=2 delims==" %%a in ('wmic os get localdatetime /value ^| find "="') do set timestamp=%%a
:: 確立檔名格式 (YYYYMMDD_HHMM)
set timestamp=%timestamp:~0,4%%timestamp:~4,2%%timestamp:~6,2%_%timestamp:~8,2%%timestamp:~10,2%

@REM 建立檔案備份
:: 檔名前綴
set "prefix=備份_"
:: 備份位置
set "BAK=%DST%\%prefix%%timestamp%"

:: 建立目錄
if not exist "%BAK%" mkdir "%BAK%"

:: 複製資料
robocopy "%SRC%" "%BAK%" /mir /unicode

@REM 複製工作完成

echo.
echo 備份工作成功: %BAK%
echo.

@REM 超出保存數量
@REM -刪除老舊備份
@REM --透過搜尋特定名稱格式,可以額外建立功能建立永久保存的備份檔

:: 依靠目錄建立時間排序
for /f "delims=" %%d in ('powershell -Command "Get-ChildItem -Path '%DST%' -Directory | Where-Object { $_.Name -like '備份_*' } | Sort-Object CreationTime -Descending | ForEach-Object { $_.Name }"') do (
  :: 提升計數
  set /a count+=1
  if !count! GTR %maxBackups% (
      echo 淘汰老舊存檔: %%d
      :: 刪除目錄
      rd /s /q "%DST%\%%d"
  )
)

echo.
echo 完成所有任務
echo.

pause

3. 後話


老實說這套系統早在筆者 2021 年開設遊戲伺服器就存在了,經過拆解變得更加彈性且易懂,在當時完全就是我的救星。畢竟知道有地圖備份,同學之間就敢互相惡搞,自然常常要復原。而這次的實作之中,我們不只複製檔案,同時還幫忙清理老舊的備份。

4. 參考


[1] robocopy — Microsoft Learn
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy

[2] How to get local date/time — stack overflow (Discussion)
https://stackoverflow.com/questions/203090/how-do-i-get-current-date-time-on-the-windows-command-line-in-a-suitable-format

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.