昔も今も変わらぬバッチファイルのお作法

2016/09/13

バッチファイル

ローカルWindows環境の外にあるインターネットの世界では、サーバーのほとんどがUNIXであり、TelnetなどWindowsで使える端末エミュレータ(ターミナル+シェル)は、UNIX(カーネル)に繋いでCUI(Character-based User Interface)ターミナル上で操作するためのものです。

プログラムの自動更新バッチファイル

バッチファイルはその名のとおり、Windowsのシェル(コマンドプロンプト)で実行させたい処理を、まとめて呼び出して走らせるための、拡張子がbatのテキストファイルで、いにしえのDosの時代から存在しました。

新卒入社したシステム会社で、Windows NT上で複数のフォルダをまるごとコピー(xcopy)する処理を書いたのがバッチファイル初体験でしたが、学生時代のPC操作の授業では、特殊記号(@, % <など)アレルギーで、意味を説明されても慣れるのにずいぶん時間がかかりました。

業務システム運用上でのバッチファイルの定番として、クライアントモジュールを起動する際に、サーバー上の最新版のバージョンをチェックして、新しければ自動アップデートする、というのがありまして、これには繰り返し(for)と条件分岐(if)と遅延環境変数の展開(enabledelayedexpansion)という、難しいシェルコマンドの作法が含まれています。

  • 1行目:@で自コマンドを表示しない。echo offで以降のコマンドも表示しない。
  • 2行目:バッチファイルが実行されているディレクトリ(カレントディレクトリ)を環境変数%CD%から取得し変数にセット。
  • 3行目:プログラムが置かれたサーバーのディレクトリのパスを変数にセット。
  • 5行目:Pingコマンド 試行1回、タイムアウト1秒
  • 6行目:環境変数errorlevelの値が1ならerrorルーチンに飛ぶ
  • 9行目:for (オプション) %%アルファベット1文字 in (ループ処理の対象) do コマンド
    in (%SVRDIR%\setting.ini):プログラムが置かれたサーバーのディレクトリにあるsetting.iniの中が対象
    /f:テキストファイルからトークン(文字列の最小単位で通常はタブ区切り文字)を取り出して代入
    "tokens=1,2 delims==" %%a:イコール("=")区切りのトークンの1番目を変数%%a、2番目を変数%%bに取得
  • 10行目:変数%%aの値が"version"であれば、変数%%bの値を変数versionに代入
  • 13行目:for (オプション) %%アルファベット1文字 in (ループ処理の対象) do コマンド
    in (ver.ini):カレントディレクトリにあるver.iniの中が対象
    /f:テキストファイルからトークン(文字列の最小単位で通常はタブ区切り文字)を取り出して代入
    "tokens=1,2 delims==" %%a:イコール("=")区切りのトークンの1番目を変数%%a、2番目を変数%%bに取得
  • 14行目:変数%%aの値が"ver"であれば、変数%%bの値を変数verに代入
  • 16行目:VerがVersionより小さければupdルーチンに飛ぶ
  • 17行目:更新成功時にはprocessルーチンに飛ぶ
  • 18行目:バッチファイルの最後に移動する。
@echo off
SET CDIR=%CD%
SET SVRDIR=\\192.168.1.100\Share_Folder

Ping 192.168.1.100 -n 1 -w 1000
if errorlevel 1 goto:error

rem サーバー上のsetting.iniと自フォルダ内のver.iniのバージョンを比較
for /f "tokens=1,2 delims==" %%a in (%SVRDIR%\setting.ini) do (
if %%a==version set version=%%b
)

for /f "tokens=1,2 delims==" %%a in (ver.ini) do (
if %%a==ver set ver=%%b
)
if %Ver% LSS %Version% goto :upd
goto:process
goto:eof


rem 更新処理ルーチン
:upd
xcopy /s/y "%SVRDIR%" %CDIR%
setlocal enabledelayedexpansion
for /f "tokens=*" %%a in (ver.ini) do (
  set var=%%a
  if not {!var!}=={!var:ver=!} set var=ver=%version%
  echo.!var! >> output.ini
)
del ver.ini
rename "output.ini" "ver.ini"
endlocal
goto:process
goto:eof


rem 更新成功時の処理ルーチン
:process
setlocal enabledelayedexpansion
start AccountPRO.lnk AccountPRO
endlocal
exit
goto:eof


rem pingエラー発生時の処理ルーチン
:error
echo you are not conected to server please check network ...
pause

サーバー上にあるsetting.iniファイル

[settings]
version=1.23

カレントディレクトリにあるver.iniファイル

ver=1.01
  • 23行目:サーバーのディレクトリをカレントディレクトリにまるまるコピー
    /sオプション:ファイルが存在する場合のみディレクトリごとコピー
    /yオプション:同名のファイルが存在する場合、上書きの確認を行わない
  • 24行目:定義した変数のローカル化(setlocal~endlocalの外に影響しない)
    enabledelayedexpansionオプション:実行中に変数の値が変化
  • 25行目:for (オプション) %%アルファベット1文字 in (ループ処理の対象) do コマンド
    in (ver.ini):カレントディレクトリにあるver.iniの中が対象
    /f:テキストファイルからトークン(文字列の最小単位で通常はタブ区切り文字)を取り出して代入
    "tokens=*" %%a:すべての文字列を変数%%aに取得
  • 27行目:変数varに"var=変数version"をセット
  • 28行目:変数verをoutput.iniファイルに書き出す。
  • 30行目:ver.iniを削除
  • 31行目:output.iniをver.iniにリネーム
  • 40行目:AccountPROのリンクをAccountPROというタイトルで開く
    startコマンドの第1引数はアプリケーションのタイトルを設定する文字列
  • 42行目:バッチファイルを途中で終了させる。
  • 43行目:バッチファイルの最後に移動する。
  • 49行目:「Please press any key to continue...」を表示させ何かしらのキー入力があるまで動作を停止。

コマンドの書き方あれこれ

Unixのカーネル(核)を操作するシェル(貝)コマンドはcygwinのようなターミナル(端末)から入力しますが、Windowsのカーネルを操作するコマンドプロンプト(cmd.exe)はコンソール(ターミナルではない)から入力します。

環境変数

Windowsのシステム環境変数の代表格Pathに、コマンドを実行するプログラムのあるディレクトリを設定することで、コマンドプロンプトからフルパスを入れずコマンドのみで実行できますが、これらシステム環境変数はWindows環境で共通なので、自分のPCで作成したバッチファイルを他人のPCで実行することができます。

一方で環境変数はユーザー自身が独自に定義することも可能で、「SET」コマンドで定義して「%環境変数%」でバッチファイル中で参照することができます。

日付と時間の取得

バッチファイルから取得する日付のフォーマットは、コントロールパネルのRegion and language(地域と言語)から設定されますが、日本式のyyyy/MM/ddと英語のM/d/yyyyとでは方法が異なります。

日本式(yyyy/MM/dd)の場合

%DATE:~-10,4% : 後ろから10桁目(-は^でエスケープされている)から4桁が年
%DATE:~-5,2% : 後ろから5桁目から2桁が月
%DATE:~-2% : 後ろから2桁目から最後までが日
%TIME:~0,2% :前から2桁が時
%TIME:~3,2% : 前から3桁目から2桁が分
%TIME:~6,2% : 前から6桁目から2桁が秒

英語式(M/d/yyyy)の場合

for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a"
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%"
set "Min=%dt:~10,2%"
set "Sec=%dt:~12,2%"

pingのエラーレベル

pingコマンドはオプションと引数のセットで指定しますが、「-n 1」で試行回数1回、「-w 1000」でタイムアウト時間を1000ミリ秒(1秒)に指定しますが、結果に応じて環境変数errorlevelに以下の値を返します。

  • ホストが見つからない:1
  • 応答なし:1
  • 応答あり:0
  • 経路なし:0

ルーターを経由しない社内LAN上のサーバーであれば「経路なし」はありえないので、errorlevelが0(応答あり)か1(応答なし)で判断できます。

エスケープシーケンス

環境変数に特殊記号を代入する場合に、エスケープ文字(^)を付ける必要があり、例えばユーザー環境変数LOGOに「&TOKYO」という文字列を入れる場合は、SET LOGO=^&TOKYO となります。