バッチファイルの書き方(その3)

(2012年4月24日追記)
バッチファイルを含む、Windows系のスクリプトについて学べるサイトをまとめました。合わせてご覧ください。


[はじめに]
いいネタがたまってきたのでバッチファイルのまとめを書こうということで、この記事を2月の中旬から書き始めたのですが気付いたら早くも一ヶ月半が経っていました。
期限を決めずにだらだらと書いていたら全然書き終わる気配がなくて、「もしかしたらこのまま日の目を見ずに終わるんじゃないか」と不安になったので急いで書き上げました。締め切りって設定しちゃうとそれに追い立てられるようで苦手でしたが、あらためて大事なことに気付かされた気がします。


[過去の記事へのリンク]

[目次]

    • 0. その前に
    • 1. ファイルをコピーするさまざまな方法
      • (1) 各コマンドの違いについて
      • (2) 何も考えずただコピーする
      • (3) 一部コピー対象から除外した上でコピーする
    • 2. プロセスを停止する方法
      • (1) プロセスが存在するかどうかを確認する
      • (2) プロセスが存在すれば停止する
    • 3.レジストリを操作する


書き方シリーズもネタがなくなってきていたのですが、最近バッチファイルをつくる機会がまた増えてきたので、忘れないうちに面白そうなものをサンプルとしてピックアップして紹介します。
それぞれの内容についての説明も書き加えますので、どなたかがバッチファイルを書く際の参考になれば幸いです。

0. その前に...

さっそく書きたいところですが、ひとつ書いておきたいことがあるのでまずはこちらについてまとめます。
これまたここ最近のことなのですが、他人の書いたバッチファイルを読む機会が増えたのですが意外に個人個人でこだわりというか癖があるもんだと気づきました。そもそもバッチファイルの書き方にそれほど幅があるとは思っていなかったのですが、その認識はあまり正しくはなかったようです。
たぶんわたしの書いたものも見る人によっては読みにくいと思いますので、わたしの書く上で気にしていることを書いておきます。

(1) コメントの書き方

バッチファイルは変数に値をセットするときと参照するときで表記の仕方が異なるので、変数が増えてくるととたんに可読性が落ちていきます。書き方の問題以上に言語しての体裁がよろしくないのでこればかりは書く側が頑張るだけでは解決しません。
とは言え、変数を使わないとメンテナンス性がとても悪くなりますので使わないわけにもいきません。
じゃあ、どうするのかというと分かりにくい部分には必ずコメントを残します。とても当たり前のことなのですが、普段CやC++,C#などでプログラムを書く時との違いはより積極的にコメントを書くということです。
なるべく該当の部分で何をやりたいのかということを記述しておけば、例え多少スクリプト自体が読みにくくてもコメントからコマンドの意味を推測したりして可読性を向上させることが出来ます。


もちろん修正があった場合にはスクリプトとコメントを直さないといけないし、どちらか一方の修正が漏れると混乱してしまうというデメリットもあるのでその点は注意が必要です。


で、コメントの書き方についてですが私の知る限りではいくつか方法があります。

REM [1] ここはコメントです

::  [2] ここもコメントみたいに扱えます

:// [3] これも[2]と同様にコメントみたいな扱いになります

:-- [4] 上に同じ

まずは[1]のREMコマンドですが、これは引数を受け取っても受け取らなくても「何もしないコマンド」です。
そのため、引数部分がコメントの役割を果たします。一般的にはバッチファイルのコメントと言えばREMコマンドを指しますのでこれを使っている人がすごく多いのですが、これがとにかく読みにくくて苦手。一見コマンドなのかコメントなのか判断しにくいのはかなりマイナスです。


次に[2]の::(コロンが2個)ですが、このコロンの後ろに書かれた文字列がコメントとなります。
で、この[2]に発想を得て以下の[3],[4]も同じようにコメントとなるんじゃないかと試してみたらそれっぽい動作をしてくれました。[3]の"://"以降や[4]の":--"以降もコメントとして扱われるようです。
ここからは推測になりますが、通常行頭にあるコロンはGOTO文の行先やCALLの呼び出し先を指定するためのラベルの役割を果たします。そのため、コロン以降の次の空白まではラベルの名前と認識されていて、さらにそのラベルと認識された場所以降(つまりここでコメントとなる文字列を記述した部分)については何にも利用されていないのではないかと考えます。
[4]のケースを例にしてテストしてみます。

@ECHO OFF

ECHO 次のステップでGOTOします

GOTO --
ECHO OK
GOTO FINISH

:-- コメントだよ
ECHO コメントに飛びました

:FINISH
PAUSE > NUL

実際に実行してみるとこんな結果になりました。

やはりラベル名として認識されているということで間違いないようです*1
つまりこのようなコメント記述方式([3]や[4]のような書き方)を複数個所に適用した場合には、そのラベルを指定したGOTOが意図しないところへ飛ぶ可能性も考えられ、少々危険であることもわかります。ただし、現実的にそんなことをする可能性は非常に低く、かなり意図的にやらない限りはこのようなミスを犯すことは考えにくいです。


可読性向上という観点からも[2]〜[4]の方式でコメントを書くことをお奨めします。

(2) エコーバックはOFFにする

他の人の書いたバッチファイルのサンプルを読んだことがある人はご存じかも知れませんが、バッチファイルの先頭によく以下のような記述を見かけると思います。

@ECHO OFF


詳細な説明については「バッチファイルの書き方(その1)の先頭に @ECHO OFF を記述」をご覧いただきたいのですが、これは実行したコマンド自体をDOS画面に表示させない時にはECHO OFFを指定するコマンドです。
わたしはサンプルを含め、どんなバッチファイルを書くときにも必ずこれを指定します。理由は以下の2点。

      • 画面に表示したい情報は作成した人でデザインすべき
      • バッチファイルが起動されるパスが長くなればなるほど見難くなる


エコーバックを有効にすると画面にはプロンプトと実行したコマンド(引数含む)が表示されます。
そのため、エラーが出た時などにどこで失敗したのかが分かりやすいので有効にしておいた方がいい....という人もいますし、わたしも以前の記事では「作成中やデバッグ中は有効にしておいたほうがいい」と書きましたがこれは間違っています。
というのも、エラーが出た場合にはすぐにDOS画面が閉じてしまうケースが多くあり、結局原因がどこにあるのかを追うことは難しいのです。通常、このような場合にはログに実行したコマンドを吐き出しておくとか、あとは適当にPAUSEを入れてどこまで進んだのかを確認しながら進めることがほとんどです。
エコーバックは常時何が実行されているのかを確認する程度の役割しかないので、その目的ですらあまり活用出来ないとなると邪魔なものでしかなくなります。エコーバックがないと嫌だとかそういう極端な嗜好の人以外はおまじないのごとく必ずつけるようにしてもよいと思います。


また、上記のとおり、エコーバックされる際にはプロンプトが表示されるのですが、ここにはバッチファイルのカレントディレクトリが表示されます。バッチファイルはデスクトップなどの分かりやすくてすぐに触れる場所において実行することがほとんどですので、自然と表示されるプロンプトは長くなりがちです。
例えばデスクトップにバッチファイルを置いて実行した場合だと、XPであれば C:\Documents and Settings\[ユーザ名]\デスクトップ となります。Vistaだと C:\Users\[ユーザ名]\Desktop となってXPよりはすっきりとしますが、決して短い文字列ではありません。
観たい情報がどうでもいいバッチファイルのカレントディレクトリ情報に埋もれてしまうのはあまりよいことではないと思いますので、やはりエコーバックはすべきではないといえます。


と言うわけでわたしはエコーバックのOFFは必ず書きます。


以上を踏まえてサンプルを読んでみてください。
というわけで長い前置きはおしまい。やっと本編に入ります。

1. ファイルをコピーするさまざまな方法

何事もそうですが、必要なファイルをあらかじめバックアップしておくということはこれは最低限のたしなみとも言える行為です。
メールやアプリケーションの設定ファイルは言うに及ばず、
バックアップとは基本的にはファイルの退避を意味しますので、それに使うコマンドはCOPYかXCOPY,RoboCopyコマンドになります。これをどのように利用するのかを順序立てて説明します。

(1) 各コマンドの違いについて

上記のとおり、ファイルをコピーするコマンドは大きく3つあります。これらの短所と長所について簡単にまとめました。
robocopyについては2003のリソースキットを入れるか、もしくはVista/Windows Server 2008からしか使えませんのでご注意ください。

コマンド名 長所 短所 特記事項
copy 1)多くのOSで利用可
2)引数も簡単なものしかないので使いやすい
3)コピー以外にも使用できる
1)xcopyよりも機能は劣る xcopyと同じ引数名で異なる機能があるため注意
xcopy 1)引数がcopyよりも充実している
2)copyでの不具合が改善されている)
特になし Vista/2008以降はxcopyよりもrobocopyが推奨されている
robocopy 1)機能が超充実
2)使いやすい
1)使えるOSが限定されている 対象OSはVista/2008以降です


使っていた期間が長かったというのも理由だと思いますが、わたしが一番好んで使うのはcopyコマンドです。xcopyの方が高機能で便利だというのは知っていますが、引数も少なくてあまり考えることの少ないシンプルなcopyが一番気軽に使えると感じますし、なかなか手放せません。
ただし上記のとおり、xcopyとcopyコマンドは似ているようで違っていて、その中でももっとも大きく異なるのが/Dオプションです。
これはcopyの/Dオプションは暗号化フォルダから非暗号化フォルダへコピーする時に暗号化を解除するよう指定するオプションですが、xcopyの/Dオプションはコピー元ファイルの方がコピー先ファイルより新しい場合のみコピーするというオプションになります。


当たり前のことですが使う前にはちゃんとマニュアルを読みましょう、ということです。


そして超高機能なrobocopyについてですが、コピー元/先のフォルダとファイル名を指定するだけのシンプルなコピーも可能ですし、ログの出し方まで指定するようなマニアックなことも可能です。ただし、まだ使えない環境の方が圧倒的に多そうなので不特定多数に向けて配布するバッチでは気軽に使うことは出来ませんが、自分自身のパソコンで必要なファイルのバックアップをするときなど、使われる環境が確実に制限出来る場合ではとても有効に使えます。


当然のことですが、使い分けがとても大事です。

(2) 何も考えずただコピーする

まずはC:\XXXXフォルダからD:\YYYYフォルダに全てのファイルをバックアップするバッチファイルを書いてみます。

@ECHO OFF

:: FROMDIRにコピー元フォルダを指定
SET FROMDIR=C:\XXXX

:: TODIRにコピー先フォルダを指定
SET TODIR=D:\YYYY

:// コピー先フォルダが無い場合は作成する
IF NOT EXIST %TODIR% (
   MD %TODIR%
)

:// コピー
XCOPY  /S /E /Y %FROMDIR% %TODIR% 


全体の流れとしては以下のとおりです。

    1. 変数FROMDIRにコピー元フォルダを指定する
    2. 変数TODIRにコピー先フォルダを指定する
    3. 変数TODIRに指定されたフォルダがあるかどうか確認し、無ければ作成
    4. 変数FROMDIRから変数TODIRにファイル/フォルダを全てコピー(同名ファイルは上書き)


コピー先フォルダにはUNCも指定出来るので、大事なファイルを定期的にバックアップする場合にも使えます。
そういう時にはXCOPYコマンドに/Yの代わりに/Dオプション(更新年月日が新しいものだけをコピーする)を指定すると、必要なファイルだけコピーされるために処理が早く済みます。

(3) 一部コピー対象から除外した上でコピーする
@ECHO OFF

SET FROMDIR=C:\XXXX
SET TODIR=D:\YYYY
SET EXCLUEFILE=%TEMP%\excludelist.txt

IF NOT EXIST %TODIR% (
   MD %TODIR%
)

:: 除外したいファイル名をリストファイルに追加する
ECHO .bmp >  %EXCLUEFILE%
ECHO .wav >> %EXCLUEFILE%
ECHO .exe >> %EXCLUEFILE%
ECHO .dll >> %EXCLUEFILE%
ECHO .txt >> %EXCLUEFILE%
ECHO .cfg >> %EXCLUEFILE%
ECHO .sql >> %EXCLUEFILE%
ECHO .lib >> %EXCLUEFILE%
ECHO .ini >> %EXCLUEFILE%

:: /EXCLUDEに除外対象ファイル名を指定してコピー
XCOPY /EXCLUDE:%EXCLUEFILE% /S /E /D /Y %FROMDIR% %TODIR% 
    1. 変数FROMDIRにコピー元フォルダを指定する
    2. 変数TODIRにコピー先フォルダを指定する
    3. 変数EXCLUEFILEにコピーしないファイルの部分一致文字列を格納する除外リストファイル名を指定します
    4. 上記EXCLUEFILEでパスを指定されたファイルに除外対象ファイルの一部を追加します
    5. 変数FROMDIRから変数TODIRにファイル/フォルダを全てコピー

(同名ファイルは新しければ上書き)


今回は/EXCLUDEオプションが主人公です。
このオプションに除外対象リストファイルのファイル名を格納するとそれに一致するファイルはコピーされません。


そもそも、このような一部のファイルのみをコピーしたいというケースがあるのかどうか疑問に思う人もいるでしょうが、普通にコピーしたいと思う回数の3倍くらいの需要はあります。全部コピーしたいなんていうケースの方がわたしは稀ではないかと思います。


例えば、VS.NETなどでソリューションファイル以下のフォルダをバックアップしたいと考えた場合を想像してみます。
ソリューション以下のフォルダをそのままバックアップしようとすると、コピーしなければいけないファイルの数やサイズ(具体的にはこちらの方が問題になりがちです)が非常に大きくなってしまいます。サイズが大きくなりがちなファイルの例としては、リンク済みのEXE/DLLやpdb(デバッグ情報格納ファイル)ファイル、あとはVC++の場合には加えてpch(コンパイル済みヘッダー)やncb(インテリセンス情報格納ファイル)が挙げられます。わたしの経験ではpchとncbファイルが特に大きくて、ちょっとしたプログラムを書いただけなのに10MByteを超えることも少なくありません。


しかしここで考えたいのは本当にバックアップしたいものは何なのか?ということです。
通常プログラマーがバックアップしたいのはソースファイルであり、それ以外のpchとかncbファイル情報などの動的に生成出来るファイルまでバックアップしたいとは思っていません。つまりこれらのファイルはバックアップする必要がないわけです。
こんな時にはここで紹介した方法でこれらコピーの必要の無いファイル名を除外対象リストに加えることでより高速にバックアップを実現することが可能となるのです。


話がバッチファイルから離れてしまいますが、バックアップで大事なことは「必要なファイルを安全で高速に退避する」ということです。バックアップと言うと、「とにかく取れるものは全部取っておく」という方もいますが、あれもこれもととにかく多くのファイルをバックアップするのが大事なのではないというのは肝に銘じて欲しいです。
時間もディスクも限られている大事なリソースなのです。


以上でファイルのコピー方法に関する説明は終了です

2.プロセスを停止する方法

Unix系OSで、ハングアップしたプロセスやゾンビとなったプロセスを停止するためにpsコマンドとkillコマンドというのがよく用いられます。わたしが大学4年で初めてlinuxを触った時に覚えた数少ないコマンドのひとつがkillコマンドでしたが、これはわたしの作ったプログラムがよく暴走することとは決して無関係ではなく*2、いつもkillのありがたみをかみ締めながらわたしの作ったプログラムを強制停止させていました。


そして会社に入ってWindowsを使うようになって、Windows系のOSはプロセスを強制停止するコマンドがないことに気付いてびっくりしました*3。タスクマネージャーを起動してプロセスを強制終了すれば同じことが出来るのですが、わざわざアプリケーションを起動しなければいけないことがどうにも不便に感じられてとても困惑したのも懐かしい思い出です。


とは言え、無いものはしょうがないのでWSHスクリプトを書いてそれを使って代用していました(こちらで紹介しています)。


そんなある日。
ふと調べてみるとXPからtasklistとtaskkillというコマンドが追加されたという情報を発見。
さっそく探して試してみるとたしかに欲しかったコマンドに違いありません。

    • tasklist → ps
    • taskkill → kill


ひとまずこれを使ってプロセスを操作してみることにしました。

(1) プロセスが存在するかどうかを確認する

まずは基本中の基本。プロセスがあるかどうかを確認する方法です。

@ECHO OFF

:: プロセス名としてバッチの第一引数を指定
SET PROCESSNAME=%1

:: 引数が無い場合はデフォルトの名前を指定
IF %PROCESSNAME%.==. (
  SET PROCESSNAME=hogehoge.exe
)

:: TASKLISTの結果をfindして、結果はNULへリダイレクト
TASKLIST | FIND "%PROCESSNAME%" > NUL

IF %ERRORLEVEL%.==1. (
  GOTO PRNOTEXIST
)

:: 見つかった場合
:PREXIST
ECHO プロセス[%PROCESSNAME%]は見つかりました
GOTO FINISH

:: 見つからなかった場合
:PRNOTEXIST
ECHO プロセス[%PROCESSNAME%]は見つかりませんでした

:FINISH
PAUSE > NUL


tasklistにはたくさん引数があるので詳しくはヘルプを見ていただきたいのですが、これらを駆使してプロセスを探した結果、プロセスが見つからなくてもtasklistコマンド自体の終了ステータスは常に0です。つまり、プロセスの有無については終了ステータスから判断することが出来ません。
そのため、ここではtasklistの結果をfindに引渡し、その終了ステータスを確認することでプロセスがあるかどうかを確認しています。
# FINDは指定文字列が見つからない場合に1を返します。見つかったら0を返します。

(2) プロセスが存在すれば停止する
@ECHO OFF

:: プロセス名としてバッチの第一引数を指定
SET PROCESSNAME=%1

:: 引数が無い場合はデフォルトの名前を指定
IF %PROCESSNAME%.==. (
  SET PROCESSNAME=FUGAFUGA.msi
)

:: TASKLISTの結果をfindして、結果はNULへリダイレクト
TASKLIST | FIND "%PROCESSNAME%" > NUL

:: 見つかった場合
IF %ERRORLEVEL%.==1. (
  ECHO プロセス[%PROCESSNAME%]が見つかったので削除します
  TASKKILL /F /IM %PROCESSNAME%
)

:FINISH
PAUSE > NUL

前半はほぼ一緒ですが、後半見つかった場合にのみTASKKILLでプロセスを停止する処理が実行されます。
こんな回りくどいことをしなくてもいきなりTASKKILLを実行しても問題ありませんが、行儀作法というか「あるかどうか分からないのにとりあえず終了処理を投げてみる」というのがどうにも気持ちが悪いのでこんな手順を踏んでいます。
このあたりは好みでよいと思います。

3.レジストリを操作する

Windowsを使う上で触れずに済ませることが出来ないもののひとつにレジストリが挙げられます。
OSの設定/動作を変えようとしたらレジストリを変更するというのは、Windowsに慣れた人には常識として身についていると思います。
ここではレジストリを操作する方法を紹介しますが、上記のとおりOSの動作を大きく変えたり、場合によってはOSにログイン出来なくなったりする可能性もありますので十分にご注意してください。

(1) レジストリをインポート/エクスポートする

レジストリをファイル経由で操作する方法として一番メジャーなのはregeditを使用する方法です。
これを使って設定値をファイルからインポートする方法とファイルにエクスポートする方法を紹介します。
# regeditの詳しい引数と類似のアプリケーション(regedt32)との違いについては以下を参考にしてください


[参考情報のまとめ]

REGEDIT [/L:system] [/R:user] [/I] [/S] filename1
REGEDIT [/L:system] [/R:user] /C filename2
REGEDIT [/L:system] [/R:user] /E filename3 [regpath]
REGEDIT [/L:system] [/R:user] [/I] [/S] filename4 [regpath]
引数 意味
/L:system SYSTEM.DAT ファイルの場所を指定します
/R:user USER.DAT ファイルの場所を指定します
filename1 レジストリに取り込むファイル (複数可) を指定します
/C レジストリを作成する元となるファイルを指定します
/I filename2 レジストリを取り込むよう指定します
/S filename2 レジストリを取り込む際に確認画面を表示しないよう指定します
/E filename3 レジストリを書き出す先のファイルを指定します
regpath 書き出しを開始するレジストリ キーを指定します
(既定値は、レジストリ全体の書き出しとなっています)


では上記を踏まえてバッチファイルを書いてみます。

@ECHO OFF

:: エクスポートするファイル
SET EXPORTFILE="%~dp0Export.reg"

:: インポートするファイル
SET IMPORTFILE="%~dp0Import.reg"


:: エクスポートする
REGEDIT /E %EXPORTFILE% HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework

:: インポートする
REGEDIT /I /S %IMPORTFILE%


エクスポートする方で使用した/Eオプションは指定されたレジストリキー以下のデータをファイルへ書き出すためのオプションです。
インポートする方で使用した/Iオプションは指定されたファイルを取り込むためのオプションです。さらに/Sオプションが付いていますが、これはサイレントモードで動作するためのオプションです。これを付けないと、インポート前にインポートしていいかどうか確認のダイアログが表示されるため、取り込むためにワンアクションが必要となります。
しかもこの確認メッセージがちょっと怖い...。



動作しなくなる場合がありますって...。たしかにその可能性はゼロではありませんが、だからといってここまで脅さなくてもいいんじゃないの?と思うわけですよ。

    • 小学校に入るといじめられる可能性があります。小学校に入りますか?
    • 就職するとリストラになる可能性があります。就職しますか?
    • 結婚すると離婚する可能性があります。結婚しますか?
    • 子どもを生むとお金がかかる可能性がありますか。出産しますか?
    • 歳をとると死ぬ可能性があります。歳をとりますか?


このような確認に意味が無いのと同じように、この確認ダイアログの脅しも意味がないと思うんですよねえ。
いや、まあ、このように既定値では自動でインポート出来ないようにしている意図も分かるだけに、せめて文言をもっと優しくして欲しかったなと思います。


まあ、基本的には信用出来ないバッチファイルやレジストリファイルは実行するべきではないのでその点は心に留めておいていただきたいです。


(2) レジストリデータをチェックする

一括でインポート/エクスポートすれば済むのであれば(1)の方法でOKですが、そんな大雑把なやり方で済むケースはそれほどありません。
例えば、何か設定値を初期登録する場合を考えてみてください。
もし既に既定値以外の値がユーザーの意思で設定されている場合にはそれを変えてしまうのはよいことではありません。あくまでデータが無かった場合にのみ登録すべきです。このような場合には上記の一括インポートでは事足りないのは明らかです。


regeditよりも細かくレジストリを操作するためにregというコマンドを利用します。


[参考]

C:\Users\itotto>reg /?

REG Operation [パラメータの一覧]

  Operation  [ QUERY   | ADD    | DELETE  | COPY    |
               SAVE    | LOAD   | UNLOAD  | RESTORE |
               COMPARE | EXPORT | IMPORT | FLAGS ]

リターン コード: (例外:REG COMPARE)

  0 - 成功
  1 - 失敗

特定の操作のヘルプについては、次のように入力してください:

  REG 操作 /?

例:

  REG QUERY /?
  REG ADD /?
  REG DELETE /?
  REG COPY /?
  REG SAVE /?
  REG RESTORE /?
  REG LOAD /?
  REG UNLOAD /?
  REG COMPARE /?
  REG EXPORT /?
  REG IMPORT /?
  REG FLAGS /?

C:\Users\itotto>reg /?


REGコマンドの各引数を簡単にまとめます。

引数 動作
QUERY レジストリを表示
ADD レジストリにデータを追加
DELETE レジストリのデータを削除
COPY レジストリデータを複写
SAVE レジストリデータをファイルに保存する
RESTORE レジストリをファイルから復元します
LOAD ファイルからレジストリデータを読み込みます
UNLOAD レジストリデータをアンロード島数
COMPARE レジストリ同士を比較します
EXPORT レジストリをエクスポートします
IMPORT レジストリをインポートします
FLAGS


上記の通り、REGは引数が非常に豊富なのですが、じつはほとんど使ったことがなくて詳しい違いを理解していないのは内緒です。
LOADとRESTOREなんて何が違うんだか分からないし、FLAGSなんてその用途自体がわからんのよねえ....。


とりあえずここでは設定されているレジストリ値を確認する方法について説明します。

@ECHO OFF

:// おまけ
for /F "tokens=1,3 delims= " %%i in ('REG QUERY HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters') DO (
:: 欲しいキーかどうか確認
  ECHO %%i | FIND /I "DataBasePath" > NUL

:: 欲しいキーの場合は使う
  IF %ERRORLEVEL% EQU 0    ECHO hostsファイルの格納場所は[%%j\hosts]です
)


このバッチファイルを実行して得られるのはhostsファイル(名前解決用のファイル)の格納場所です。
実はわたしも先日まで知らなかったのですが、hostsファイルの格納場所と言うのはユーザが変えることが出来ます。ずっと %SYSTEMROOT%\system32\drivers\etc 固定なのだと思っていましたが、レジストリ(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parametersハイブ以下のDatabasePathというキーの値)に格納フォルダが指定されているようです。


これを確認するためのバッチファイルですが、処理の流れは以下のとおりです。

    1. REG QUERYを実行して目的のハイブ以下のキーとその値をすべて取得
    2. 1.の結果をFORに渡してひとつずつ標準出力を取得
    3. 空白区切りで1番目の列が「DatabasePath」というキーかどうかを確認
    4. 3.が目的のキーでなければ次のデータを読み込む
    5. 3.が目的のキーであればそのキー値を利用してhostsファイルへの絶対パスを表示


制御文の弱いDOSコマンドにおいて、FORは非常に便利なのでぜひ活用してください。



とりあえず今回もだいぶ長くなったのでこれくらいにします。
あと書き残しているのはタスクにジョブを登録する方法とか、runasコマンドの活用、Kernelバージョン6.0以降で使えるようになったコマンドについてくらいかな。そのあたりについてもネタがたまってまた気が向いたら書こうと思います。とりあえず夏くらいが目標かな。

*1:ちなみに[2]の方はラベル名として認識はされませんでした

*2:ログを書き出すプログラムのテストを流しっぱなしで帰ってしまい、ディスクが枯渇してしまったこともあります。いくら初心者だったとは言え、ひどいプログラムばかり作っていたなと反省せずにはいられません

*3:当時はWindows NTと2000が主流でした