Fragments Of Time

Happiness only real when shared.

Differential backup using 7-Zip for Windows (Part 1) - 利用 7-Zip 進行差異備份(上篇)

簡介

本文介紹利用 Open Source 軟體 7-Zip 壓縮程式,來進行差異備份。使用 Windows/DOS 的 cmd batch
批次檔案,自動為備份檔名附加日期時間戳記。並於(下篇)中說明使用 cmd batch 批次指令時,遇到的各種問題(陷阱)與解決方法。

[][3]

前言

就像 MS Windows 作業系統是封閉系統一樣,它的備份程式,也一樣封閉,尤其是在每次作業系統改朝換代時,系統提供的備份程式就重新換上一套。為了不受限於作業系統或任何軟體的封閉格式,同時養成經常備份的習慣,一直想用
7-Zip 這個優良、壓縮率極高的 Open Source 軟體來做備份。

只是若每次備份都包含所有的檔案,至少會有以下這些缺點:

  • 備份時間過長
  • 備份檔案過大
  • 無法分辨異動的部分

前面兩點會嚴重影響備份的意願,也增加保留備份檔案的困難。至於最後一點,如果不使用 WinMerge 這類內容比對程式,在解開備份檔案後,手動進行比對,實在是很難分辨到底有哪些檔案有異動,增加了還原工作的困難。

7-Zip 支援差異備份

有可能讓 7-Zip 只備份異動的部分嗎?讓 7-Zip 進行 Incremental backup(增量備份)Differential
backup(差異備份)
嗎?答案是肯定的:7-Zip 支援差異備份。在 Automated differential backup using
7zip for linux/windows
這篇文章中,作者介紹了 7-zip 支援差異備份的指令用法。至於詳細的 7-Zip command
line 程式 7za 的安裝與用法,請參考 7-Zip Command-Line 這篇文章。

有關於增量備份與差異備份的不同的詳細介紹,請參考前面 Wikipedia 的連結或「增量備份和差異備份的不同!」這篇文章的說明。基本上,本文所介紹的差異備份,只需要保留一份完整備份,以及最後一份基於該完整備份的差異備份檔案即可。不過,若是保留每次的差異備份,則可以隨時回溯到過去的備份。

功能與需求

本文要介紹的 cmd batch 內容,主要是基於以下的個人備份需求:

  • 可以分類(分開)備份不同的目錄
  • 可以自動備份常用的目錄
  • 可以手動備份指定的目錄
  • 可以自動為備份檔案名稱,加上時間戳記
  • 可以指定進行完整備份或差異備份
  • 進行完整備份時,若已有當天的完整備份,則改進行差異備份
  • 進行差異備份時,若找不到完整備份,則改進行完整備份
使用方式
設定

找到批次檔中 :CONFIG 部分,修改你個人的預設設定:

     :CONFIG

     set BAK_DIR=[你要儲存備份檔的目錄,不要包含路徑最後的 \ 字元] set SRC_DIR=[你要備份的目錄,不要包含路徑最後的 \ 字元。用空格或逗號區隔不同目錄。如果目錄包含空白字元,請使用 "" 括住完整路目錄名稱] 
執行完整備份

在 cmd 命令列模式下,執行下列指令:

  ` 7z-bak FULL `
 可以自行指定要備份的來源目錄:  
  ` 7z-bak FULL C:\games C:\downloads `
 建議使用自動排程工具,設定定期執行。譬如每週執行一次完整備份。
執行差異備份

在 cmd 命令列模式下,執行下列指令:

  ` 7z-bak DIFF `
 因為預設執行差異備份,所以 DIFF 參數可以省略:     
  ` 7z-bak `
 同樣也可以自行指定要備份的來源目錄:     
  ` 7z-bak C:\games C:\downloads `
 建議使用自動排程工具,設定定期執行。譬如每天執行一次差異備份。
執行還原備份
直接使用 7-Zip 視窗介面

使用 7-Zip 視窗介面打開要還原的完整備份檔案,譬如 doc_20120701.7z,解壓縮到希望的目錄。然後打開要還原的差異備份檔案,注意檔名前面的日期戳記要與完整備份一致,如
doc_20120701_diff_20120720_025643.7z,選擇解壓縮到同一個目錄,過程中會詢問是否要取代原有的檔案,請選擇全部覆蓋(Yes
to All
)。

使用 7-Zip 命令列介面

在 cmd 命令列模式下,執行下列指令:

     7za.exe x doc_20120701.7z -oc:\recovery_path\ 7za.exe x doc_20120701_diff_20120720_025643.7z 
    -aoa -y -oc:\recovery_path\ 
限制,與可以改進的地方
  • 不支援讀取外部設定檔的功能。BAK_DIR 與 SRC_DIR 必須在批次檔中設定,不過 SRC_DIR 可以使用命令列參數取代。
  • 不支援指定備份檔案類型,及排除檔案類型功能。
  • 單一備份的來源必須在同一個目錄下,不支援將多個來源目錄壓縮到單一備份檔中。
  • 未檢查實際上是否有異動檔案,即直接進行差異備份,可能導致差異備份檔案中空無一物。
  • 尚未提供還原功能。

讀者若有興趣嘗試 DIY,可以根據自己的需求修正上述限制,增加備份彈性。

批次檔原始碼

完整的批次檔指令稿內容如下:

     @echo off & goto CONFIG

     :SYNTAX

     echo Syntax: %~n0 [DIFF|FULL] [folders] goto END

     :CONFIG

     set BAK_DIR=[你要儲存備份檔的目錄,不要包含路徑最後的 \ 字元] set SRC_DIR=[你要備份的目錄,不要包含路徑最後的 \ 字元。用空格或逗號區隔不同目錄。如果目錄包含空白字元,請使用 "" 括住完整路目錄名稱]

     set Z_OPT=-scsUTF-8 -ssc -ssw -ms=on -mx=9 -t7z

     goto START

     :PARAMS SETLOCAL set PARAM= for %%F in (%*) do ( if "FULL" == "%%F" ( set TYPE=FULL ) else if "DIFF" == "%%F" ( set TYPE=DIFF ) else ( if "" == "!PARAM!" ( set PARAM=%%F ) else ( set PARAM=!PARAM!, %%F ) ) ) ENDLOCAL & set "TYPE=%TYPE%" & set "PARAM=%PARAM%" goto :eof

     :TIMESTAMP SETLOCAL for /F "usebackq tokens=1,2 delims==" %%i in (`wmic os get LocalDateTime /VALUE 2^>NUL`) do if ".%%i."==".LocalDateTime." set ldt=%%j ENDLOCAL & set "DATE=%ldt:~0,8%" & set "TIME=%ldt:~8,6%" goto :eof

     :DO_FULL_BAK SETLOCAL set DIR=%1 set FULL=%2 echo ...performing full backup... 7za a %FULL% %Z_OPT% %DIR% echo . echo ...%FULL%...done^^! ENDLOCAL goto :eof

     :DO_DIFF_BAK SETLOCAL set DIR=%1 set FULL=%~f2 set DIFF="%FULL:~0,-3%_diff_%DATE%_%TIME%.7z" echo ...performing diff backup on "%FULL%"... 7za u "%FULL%" %Z_OPT% -u- -up0q3r2x2y2z0w2^^!%DIFF% %DIR% echo . echo ...%DIFF%...done^^! ENDLOCAL goto :eof

     :FULL SETLOCAL set SRC="%~f1" set FN=%~nx1 echo ------------------ echo Requesting full backup for %SRC%... set FB="%BAK_DIR%\%FN%_%DATE%.7z" if EXIST %FB% ( echo ...full backup for %SRC%: %FB% already exist, re-request diff backup instead... call :DO_DIFF_BAK %SRC% %FB% ) else ( call :DO_FULL_BAK %SRC% %FB% ) ENDLOCAL goto :eof

     :DIFF SETLOCAL set SRC="%~f1" set FN=%~nx1 echo ------------------ echo Requesting diff backup for %SRC%... echo ...finding full backup... set FB= for %%N in (%BAK_DIR%\%FN%_*.7z) do ( set STMP=%%N set STMP=!STMP:~-11,8! if "%%N" == "%BAK_DIR%\%FN%_!STMP!.7z" ( echo ......found: "%%N" set FB="%%N" ) ) if "" == "%FB%" ( echo ...full backup not found, re-request full backup instead... call :DO_FULL_BAK %SRC% "%BAK_DIR%\%FN%_%DATE%.7z" ) else ( echo ...full backup found: %FB% call :DO_DIFF_BAK %SRC% %FB% ) ENDLOCAL goto :eof

     :START

     SETLOCAL enabledelayedexpansion

     call :PARAMS %* if NOT "" == "%PARAM%" ( set SRC_DIR=%PARAM% ) if "" == "%TYPE%" ( set TYPE=DIFF ) echo BAK_DIR=%BAK_DIR% echo SRC_DIR=%SRC_DIR% echo TYPE=%TYPE%

     call :TIMESTAMP echo date: "%DATE%" echo time: "%TIME%"

     for %%F in (%SRC_DIR%) do ( call :%TYPE% %%F )

     :END 
 請自行複製上面的程式碼,儲存到 7z-bak.cmd 或任何你覺得適當的檔名,最好把檔案放到 PATH 路徑下,方便執行。
結論

完成了 7z-bak.cmd 之後,利用自動排程工具,自動定時備份,確實是相當方便。意外的驚喜是,7-Zip 進行差異備份的速度,實在是快得驚人,讀者不妨自行體驗一下。

最後,不得不承認,使用 Windows/DOS 內建的 cmd 批次檔功能來處理自動備份,實在是個蠢主意。 為什麼這麼說呢?在下一篇,將說明撰寫這個批次檔過程中所遇到的問題與陷阱,想要了解技術細節的朋友,請不要錯過。

歡迎大家的回饋與心得分享。

參考資料:
相關文章:

[3]: