Windows TIPS
[Scripting]
バッチ・ファイル中で日付をファイル名に使用する
→ 解説をスキップして操作方法を読む
デジタルアドバンテージ
打越 浩幸、島田 広道
2004/05/01
2011/08/26更新
対象OS
Windows 2000
Windows XP
Windows Server 2003
Windows Vista
Windows Server 2008
Windows 7
Windows Server 2008 R2
■
バッチ・ファイル中で日付や時間をベースにしたファイル名を利用したい場合がある。
■
このような用途では、環境変数の%date%や%time%を利用して、ファイル名を合成すればよい。
■
ただし、ファイル名に使えない文字は取り除きつつ、日付と時刻の情報を抜き出すテクニックが必要になる。
バッチ・ファイル中で、日付や時間をベースにしたファイル名を利用したい場合は少なくない。例えばシステム・データやユーザー・ファイルをバックアップし、バックアップ先のフォルダやファイル名に、その日の日付を反映した名前(例:「systemconfig-20110824」や「user1-110824」など)を付けたいといった場合である。
このような目的のためには、環境変数の「date」や「time」を使えばよい。それぞれ、今日の日付と現在の時刻を文字列にして返してくれる(以下はWindows XP以降のWindows OSでの実行例)。
C:\>echo %date%
2011/08/24
C:\>echo %time%
14:54:08.22
だがこれらの変数の返す値には、「/」や「:」といった、ファイル名としては利用できない文字が含まれている。ファイル名を生成する場合にはこれらの文字を取り除き、数字の部分だけを使いたい。
さらにWindows 2000の場合は次のように、日付の直前に曜日を表す文字が表示されるので、これも取り除いて処理する必要がある。そのためには、文字列の最後尾から10文字だけを取り出せばよいだろう。
C:\>echo %date%
水 2011/08/24
本稿では、Windows 2000からWindows 7/Windows Server 2008 R2までを対象に、バッチ・ファイルでdateやtime変数から日付と時刻の情報を抜き出して利用するためのテクニックを紹介する。
環境変数から数字部分を抽出する
dateやtime変数から、数字の部分だけを取り出すには、単に「%date%」「%time%」とするのではなく、変数値の部分文字列抽出用の修飾を行えばよい。具体的には、ある変数Vの、先頭(文字列の左端)からm番目の文字からn文字分を取り出すには、「%V:~m,n%」とする(mは0から始まる)。例えば変数Vの値が「ABCDEFGHIJKL」の場合、6文字目から2文字分取り出すには「%V:~5,2%」とすればよい(先頭文字は0番目と数えるので、「6」ではなく「5」を指定する)。
C:\>set V=ABCDEFGHIJKL
C:\>echo %V:~5,2%
FG
nを省略すると、位置mから最後尾(文字列の右端)までとなる(例:「%V:~5%」なら「FGHIJKL」となる)。mに負の数を指定すると、先頭ではなく、最後尾から数えた文字位置になる(「%V:~-3%」ならば最後の3文字「JKL」になる)。またnに負の数を指定すると、最後のn文字を除いた部分の文字列となる(位置mからn文字目の直前まで。例えば「%V:~5,-3%」ならば「FGHI」となる)。まとめると、次のようになる。
書式
意味
例
%V%
変数Vの値全体
「%V%」 ⇒ 「ABCDEFGHIJKL」
%V:~m%
m文字目から、最後まで
「%V:~5%」 ⇒ 「FGHIJKL」
%V:~m,n%
m文字目から、n文字分
「%V:~5,2%」 ⇒ 「FG」
%V:~m,-n%
m文字目から、最後のn文字分を除いたもの
「%V:~5,-2%」 ⇒ 「FGHIJ」
%V:~-m%
後ろからm文字目から、最後まで
「%V:~-5%」 ⇒ 「HIJKL」
%V:~-m,n%
後ろからm文字目から、n文字分
「%V:~-5,2%」 ⇒ 「HI」
%V:~-m,-n%
後ろからm文字目から、最後のn文字分を除いたもの
「%V:~-5,-2%」 ⇒ 「HIJ」
%V:s1=s2%
文字列s1を文字列s2に置換する。s2を省略すると、s1の削除になる
「%V:ABC=abc%」 ⇒ 「abcDEFGHIJKL」
変数の部分文字列の抽出と置換
変数の値全体を参照するには「%V%」とするが、変数名の修飾子として「:~<数字> 」を付けることにより、文字列の一部分だけを取り出すことができる。ここではVの値は「ABCDEFGHIJKL」としている。修飾子の詳しい解説はコマンド・プロンプト上で「set /?」を実行すると表示される。
従って、「水 2011/08/24」のような文字列の入ったdate変数から数字の部分だけを抜き出すには、
年: 最後尾より10文字目から、4文字分
月: 最後尾より5文字目から、2文字分
日: 最後尾より2文字目から、2文字分
すなわち、
「%date:~-10,4%%date:~-5,2%%date:~-2,2%」
とすればよいことが分かるだろう(年と月と日の部分をそれぞれ抜き出して結合している)。
C:\>echo %date%
水 2011/08/24
C:\>echo %date:~-10,4%%date:~-5,2%%date:~-2,2%
20110824
この方法であれば、Windows 2000のようにdate変数の先頭に曜日が入る場合でも、あるいはWindows XP以降のWindows OSのように曜日が入らない場合でも、そのまま利用できる。
なお、コントロール・パネルの[地域と言語]アプレット(あるいは[地域と言語のオプション]、[地域のオプション])で、日本以外の地域を選択したり、あるいは日付と時刻の形式をデフォルトから変更したりすると、数字の位置が変わってしまい日付や時刻が正しく抜き出せないことがあるのでご注意いただきたい。
時刻の計算
日付の場合と同様に、時刻(時分秒)の場合にも少し注意点がある。午前0時から午前10時(00:00:59〜9:59:59)の場合、time変数が返す文字列の先頭には、「0」ではなく、空白文字が含まれている(00、01、02……、09、10、11、……ではなく、0、1、2、……、9、10、11、……となる)。つまり時(0〜23)の数値はゼロサプレスされて表示されている。そのため、時間を取り出すために、単純に「%time:~0,2%%time:~3,2%%time:~6,2%」とすると、先頭に空白文字が入っている可能性がある。これをそのままファイル名やバッチ・ファイルのパラメータとして利用すると、空白文字によって引数の区切りとして扱われるなどの不具合が生じる可能性がある。
C:\>time 1:23
C:\>echo %time%
1:23:04.29
C:\>time 12:34
C:\>echo %time%
12:34:01.60
このような不具合を防ぐためには、time変数に含まれる空白文字を、最初に数字の0に置き換えておけばよいだろう。変数の置換は先の表にあるように、「%V:s1=s2%」とすればよい。ここでs1には空白1文字を、s2には「0」を指定する。
C:\>echo %time:~0,2%%time:~3,2%%time:~6,2%
11204 …置換しない場合は、このように先頭に空白文字が含まれる
C:\>set time2=%time: =0% …いったん一時変数に入れて置換する
C:\>echo %time2:~0,2%%time2:~3,2%%time2:~6,2%
011204 …先頭に空白文字は含まれず、必ず数字が入る
なお、「%time%」の内容をいったん変数time2に入れてから次の行で2桁ずつ取り出しているのは、文字列の置換と部分抽出を同時に行えないからである。
実際の利用例(バックアップした日がすぐ分かるようにフォルダをバックアップする)
この手法を利用して、例えばrobocopyコマンドで過去のバックアップを残しつつ、特定のフォルダをバックアップするには次のようにする。バックアップの保存先としては、このバッチ・ファイルを起動したドライブの「\Backup」というフォルダの直下に、バックアップした日付を含む名前のサブフォルダを作成して利用する。これにより過去のバックアップは上書きされずに別々のサブフォルダに保存されていく。また、このサブフォルダをどこか別のシステムへコピーして保存することを考え、サブフォルダ名にはサーバ名も含めるようにしている。これにより、どのシステムでいつバックアップしたかがすぐに分かるようになる。
※ファイル: backupcmd.bat
setlocal
set drv=%cd:~0,1%
set dt=%date%
set FName=%drv%:\Backup\%dt:~-10,4%%dt:~-5,2%%dt:~-2,2%-%COMPUTERNAME%
robocopy c:\datafiles "%FName%" /mir
endlocal
「set drv=%cd:~0,1%」は、カレント・ドライブを取り出すための指定である。タスク・スケジューラで自動起動する場合は、カレント・ドライブではなく、「C:」や「D:」など、特定のドライブを明示的に指定するのがよい。
「set dt=%date%」で、いったんdate変数をdtという変数にコピーしているのは、date変数を3回参照している間に日付が変わってしまっても問題がないようにするためである。このテクニックはdate変数より、むしろtime変数を使う場合に有用だろう。なぜなら、処理に時間がかかるとtime変数の値はどんどん進んでしまい、不整合が生じる可能性があるからだ。なお、「%date%」を実行してから「%time%」を実行するまでの間に日付が変わる可能性もわずかながらあるが、これが心配なら、次のようにIF文を追加で実行して、「%date%」を再取得するとよいだろう。
set dt=%date% & set tm=%time%
if "%tm:~0,5%"==" 0:00" set dt=%date%
※00時00分の場合は、日付が前日の可能性があるので再取得する
1日前の計算
ところで以上の例では今日の日付を取り出してフォルダ名の一部に用いているが、実際には、前日の日付がほしい場合も少なくない。例えば前日分のログファイルを別のサーバへコピーしたり、1カ月前のファイルを見つけ出して削除したり、といったケースが考えられる。残念ながら1日前とか1カ月前を計算して、変数にセットする簡単な方法はない。一応「set /aコマンド」を使えば数値計算もできるので、日付部分を1日前に戻すといった操作も不可能ではないが、月の初めや年の初め、うるう年の2月末日の処理なども考慮しなければならないので、非常に面倒である。あえてバッチ・ファイルで書くとすると、次のようになるだろうか。
※ファイル:prevdate.bat
setlocal
set dt=%date%
set yy=%dt:~-10,4%
set mm=%dt:~-5,2%
set dd=%dt:~-2,2%
echo 今日は%yy%年、%mm%月、%dd%日です。
echo.
rem 1日前の日付を計算する
set /a dd=1%dd%-101
set dd=00%dd%
set dd=%dd:~-2%
set /a ymod=%yy% %% 4
if %dd%==00 (
if %mm%==01 (set mm=12 & set dd=31 & set /a yy=%yy%-1)
if %mm%==02 (set mm=01 & set dd=31)
if %mm%==03 (set mm=02 & set dd=28 & if %ymod%==0 (set dd=29))
if %mm%==04 (set mm=03 & set dd=31)
if %mm%==05 (set mm=04 & set dd=30)
if %mm%==06 (set mm=05 & set dd=31)
if %mm%==07 (set mm=06 & set dd=30)
if %mm%==08 (set mm=07 & set dd=31)
if %mm%==09 (set mm=08 & set dd=31)
if %mm%==10 (set mm=09 & set dd=30)
if %mm%==11 (set mm=10 & set dd=31)
if %mm%==12 (set mm=11 & set dd=30)
)
echo 1日前は、%yy%年、%mm%月、%dd%日です。
endlocal
だが、ここまでしてわざわざバッチ・ファイルで計算するのは、あまり勧められない。n日前にするといった応用が利かないからだ。こういった場合はWSHなどを呼び出して処理する方が簡単かもしれない。WSHを呼び出す方法については、TIPS「曜日や日付によって処理を切り替える(BAT File) 」などを参考にしていただきたい。
この記事と関連性の高い別のWindows TIPS
generated by
更新履歴
【2004/05/05】 当初、%date%変数から数字の部分を抜き出す方法として「%date:~0,4%%date:~5,2%%date:~8,2%」を紹介しておりましたが、Windows 2000環境では日付文字列の先頭に曜日が表示されるのでこれでは正しく動作しないことが判明しました。そこでいったん「%date:~-10%」として、数字部分だけを取得する方法に変更し、関連する解説文を追加・変更しました。お詫びして訂正させていただきます。
【2007/10/12】 %time%変数の取り扱いに関する注意点を追加しました。
【2011/08/26】 「1日前の計算」のprevdate.batにて、日付が8日または9日のときにエラーが発生する不具合と、Windows 2000で年月日を正しく抽出できなかった不具合を修正しました。以上、お詫びして訂正いたします。また、Windows Server 2008/Windows 7/Windows Server 2008 R2での動作を確認しました。さらに、例として採りあげていたntbackupコマンドがWindows Vista以降で利用できなくなったため、代わりにrobocopyによるフォルダ・バックアップの例に差し替えました。
TechTargetジャパン
Windows Server Insider フォーラム 新着記事
キャリアアップ