ここまで説明した手法を利用して、robocopyコマンドで過去のバックアップを残しつつ、特定のフォルダをバックアップする例を説明しよう。robocopy自体については、Tech TIPS「Windowsの『robocopy』コマンドでフォルダをバックアップ/同期させる」を参照していただきたい。
バックアップの保存先としては、このバッチファイルを起動したドライブの「\Backup」というフォルダの直下に、バックアップした日付を含む名前のサブフォルダを作成して利用する。これにより過去のバックアップは上書きされずに別々のサブフォルダに保存されていく。
また、このサブフォルダをどこか別のシステムへコピーして保存することを考え、サブフォルダ名にはサーバ名も含めるようにしている。例えばファイル名の最後に「-%COMPUTERNAME%」などを付けてコンピュータ名を付加する。これにより、どのシステムでいつバックアップしたかがすぐに分かるようになる。
@echo off
setlocal
rem バックアップするフォルダ
set src_dir=c:\datafiles
rem カレントドライブを取得
set drv=%cd:~0,1%
rem 日付を確定するために別の環境変数へセット
set dt=%date%
rem 保存先フォルダ名に日付と時刻、コンピュータ名を加える
set dest_dir=%drv%:\Backup\%dt:~0,4%%dt:~5,2%%dt:~8,2%-%COMPUTERNAME%
robocopy "%src_dir%" "%dest_dir%" /mir
「set drv=%cd:~0,1%」は、カレントドライブを取り出すための指定である。タスクスケジューラで自動起動する場合は、カレントドライブではなく、「C:」や「D:」など、特定のドライブを明示的に指定するのがよい。
「set dt=%date%」で、いったんdate変数をdtという変数にコピーしているのは、date変数を3回参照している間に日付が変わってしまっても問題がないようにするためである(バッチファイルでは、1行ごとに環境変数の展開処理が行われるので、行が異なると、%date%や%time%の値が変わっている可能性がある)。
このテクニックはdate変数より、むしろtime変数を使う場合に有用だろう。なぜなら、処理に時間がかかるとtime変数の値はどんどん進んでしまい、不整合が生じる可能性があるからだ。
なお、「%date%」を実行してから「%time%」を実行するまでの間に日付が変わる可能性もわずかながらある。それが心配なら、次のようにif文を追加で実行して、「%date%」を再取得するとよいだろう(日付をまたぐような時間帯には実行させないという対処方法もある)。
rem 00時00分の場合は、日付が前日の可能性があるので再取得
set dt=%date%& set tm=%time%
if "%tm:~0,5%"==" 0:00" set dt=%date%
以上の例では、今日の日付を取り出してフォルダ名の一部に用いている。しかし実際には、前日や1カ月前の日付が欲しい場合も少なくない。
例えば、前日の日付を名前として持つログファイルを別のサーバへコピーしたり、1カ月前のファイルを見つけ出して削除したり、といったケースが考えられる。
しかし、残念なことにバッチファイルでは、1日前や1カ月前を計算して、変数にセットする簡単な方法はない。
一応「set /a」コマンドを使えば数値計算もできるので、日付部分を1日前に戻すといった操作も不可能ではない。しかし、月の初めや年の初め、うるう年の2月末日の処理なども考慮しなければならないので、非常に面倒である。
あえてバッチファイルで書くとすると、次のようになるだろうか。
@echo off
setlocal
rem 現在の日付を取得
set dt=%date%
set /a year=%dt:~0,4%
set /a month=%dt:~5,2%
set /a day=%dt:~8,2%
echo 今日は、%year%年、%month%月、%day%日です。
rem うるう年を判定するための変数を用意
set /a year_mod4=%year% %% 4
set /a year_mod100=%year% %% 100
set /a year_mod400=%year% %% 400
rem 平年の2月末日
set end_of_feb=28
rem 年が4で割り切れるなら、うるう年
if %year_mod4%==0 set end_of_feb=29
rem ただし、100で割り切れて400で割り切れない年は、平年
if %year_mod100%==0 if not %year_mod400%==0 set end_of_feb=28
rem 今日の日付から1を引く
set /a day-=1
rem 1日前が「0」、つまり今日が月初日なら前月末日をセット
if %day%==0 set /a month-=1
if %day%==0 (
if %month%==0 set /a year-=1,month=12,day=31
if %month%==1 set /a day=31
if %month%==2 set /a day=%end_of_feb%
if %month%==3 set /a day=31
if %month%==4 set /a day=30
if %month%==5 set /a day=31
if %month%==6 set /a day=30
if %month%==7 set /a day=31
if %month%==8 set /a day=31
if %month%==9 set /a day=30
if %month%==10 set /a day=31
if %month%==11 set /a day=30
)
echo 1日前は、%year%年、%month%月、%day%日です。
rem 月日の先頭をゼロで埋めて2桁に固定
set mm=00%month%
set mm=%mm:~-2%
set dd=00%day%
set dd=%dd:~-2%
echo ファイル名のための文字列: %year%%mm%%dd%
しかし、ここまでしてわざわざバッチファイルで計算するのは勧められない。複雑で分かりにくく、また2日前や3カ月前にするといった応用が難しいからだ。
こういった場合は、機能の豊富なPowerShellやWSHで処理する方がむしろ簡単だろう。例えばPowerShellでは、年月日を数字にするなら以下のようにするだけだ。
(Get-Date).ToString("yyyyMMdd")
1日前を求めるには、日付を加減算するAddDays()メソッドを使うと、わずか1行で済む。バッチファイルとは大違いだ。
(Get-Date).AddDays(-1).ToString("yyyyMMdd")
PowerShell自体については、PowerShell連載「Windows PowerShellコマンド&スクリプティング入門」を参照していただきたい。
WSH(VBScript)での実現方法については、Tech TIPS「Windowsのバッチファイルで曜日や日付によって処理を切り替える(VBScript併用編)」を参照していただきたい。
■関連記事
■更新履歴
【2022/12/21】Windows 11などに対応しました。
【2018/03/15】最新OSに合わせて内容を更新しました。
【2015/07/01】Windows 8/Windows 8.1/Windows 10/Windows Server 2012/Windows Server 2012 R2に対応しました。
【2004/05/01】初版公開。
Copyright© Digital Advantage Corp. All Rights Reserved.