DOSのコマンドだけでは出来ないことをバッチファイルで実行する方法


最近バッチファイルを使う機会がめっきりと減っていますが、それでもコンパイラいらずというメリットはとても大きくてとても重宝しています。例えば、サーバーで作業をしている時にどうしても明日の朝に一度だけ実行したい処理が出てきたときなどは、その処理をバッチファイルで書いてタスクに登録しておけば対応することが可能となります。


さて。そんなバッチファイルの一番の問題点は、OSごとに使えるコマンドが違うという点にあります。
例えば上で例にあげたタスクの登録ですが、これは「schtasks」というコマンドを使うことでコマンドからタスクの登録が可能となります。ですが、実際にこのコマンドが使えるのはWindows2003からであり、2000以前の場合にはこのコマンドは使えません。古い環境では機能の劣るatコマンドを使うしかないのです。


また、Windows XP以降ではプロセスの一覧を取得するコマンドとして「tasklist」というコマンドが利用できますが、2000以前のコマンドプロンプトではプロセス一覧を取ることが出来ないのです。こちらについては代替案すらありません。


こういったOSごとの差異をバッチファイルだけ吸収するのはとても面倒ですが、だからといって専用のコマンドをCやC++で書くのも余計面倒です。もしくは動作させる対象の環境を限定してしまえば細かいことは考慮しなくてもいいのですが、使えないケースがあるというのは意外に不便です。
ではどうすればよいのかとあれこれ考えて出した結論は、WSHを生成して実行すればいいんじゃね?ということです。
具体的にどうやるのかということについてはプロセスの一覧取得のバッチファイルを例に説明します。

@ECHO OFF

SET SCRFILE="%TEMP%\tmptasklist.vbs"

ECHO Wscript.echo "イメージ名                     PID セッション名     セッション# メモリ使用量" > %SCRFILE%
ECHO Wscript.echo "========================= ======== ================ =========== ============" >> %SCRFILE%
ECHO. >> %SCRFILE%
ECHO Set objWMIProcess = GetObject("winmgmts:").InstancesOf("Win32_Process") >> %SCRFILE%
ECHO For Each myProcess In objWMIProcess >> %SCRFILE%
ECHO         If myProcess.SessionId = 0 Then >> %SCRFILE%
ECHO 		SSNAME = "Services" >> %SCRFILE%
ECHO 	Else >> %SCRFILE%
ECHO 		SSNAME = "Console" >> %SCRFILE%
ECHO 	End If >> %SCRFILE%
ECHO 	Wscript.echo myProcess.Name ^& vbTab ^& myProcess.ProcessId ^& vbTab ^& SSNAME ^& vbTab ^& myProcess.SessionId ^& vbTab ^& myProcess.WorkingSetSize >> %SCRFILE%
ECHO Next >> %SCRFILE%

:: デバッグ用
:: NOTEPAD %SCRFILE%

CSCRIPT %SCRFILE%


これはプロセスの一覧を表示させるためのVBScriptである以下のスクリプトを生成して実行しているだけなのですが、出力結果はおおむねTASKLISTと同じになります。

Wscript.echo "イメージ名                     PID セッション名     セッション# メモリ使用量" 
Wscript.echo "========================= ======== ================ =========== ============" 
 
Set objWMIProcess = GetObject("winmgmts:").InstancesOf("Win32_Process") 
For Each myProcess In objWMIProcess 
        If myProcess.SessionId = 0 Then 
		SSNAME = "Services" 
	Else 
		SSNAME = "Console" 
	End If 
	Wscript.echo myProcess.Name & vbTab & myProcess.ProcessId & vbTab & SSNAME & vbTab & myProcess.SessionId & vbTab & myProcess.WorkingSetSize 
Next 


ポイントというほどのことは特にないのですが、注意した方がよい点として特殊文字エスケープが挙げられます。
上記のバッチファイルでは、 & という文字を表示するために ^& という表記をしています。これは & が直前のコマンドが正常実行された場合に次のコマンドを実行するという特殊な記号を表しており、これをエスケープするために^という文字を使っています。
同様に | もエスケープが必要な文字です。


ちなみに変数を表す % をエスケープする場合には % を重ねることでエスケープされます。


データの表示に限って言えば、上記の例に挙げたWMIを使うことでやりたいことのほぼ大半は出来ます。
スクリプトスクリプトを生成するというのは常套手段ではありますが、バッチファイルでもこの手法はお勧めですし、今回のようにファイルに一度保存しておくことでデバッグもやりやすくなりますのでこの流れは覚えておくと使いまわせます。


[追記]
書き忘れたのですが、注意事項としてはWSHのバージョンが違うOSや、そもそもWSHが使えないOSというのもありますので、そういったOSまで含めて考える場合にはさすがにWin32アプリケーションを作るのがよいと思います。