他の端末のプロセスとしてバッチファイルを起動したい


昔からバッチファイルでやりたいと思っていたことがひとつありまして、それは任意のバッチファイルを他のコンピュータに実行させるということです。つまり自分がいま使っている端末で何かを動かすのではなく、ネットワークの向こうにある端末側のプロセスとしてバッチファイルを起動させたいと思っていたのです。


当初はそんなのは簡単に出来そうだとなめていましたが、WindowsUnix/Linux系OSと違ってrshやsshサービスが動いているわけでもありませんし、これに類似した使えそうなサービスはtelnetだけという悲しい状況に気付いてからは簡単に出来る方法は無さそうだと諦めていました。
もちろん適当なサービスを作って動かせば実現出来るでしょうが、そうじゃなくて手軽なスクリプトでやりたいんですよねえ。プログラムを作らずにやりたいのです。
なんて、「そんなことが簡単に出来たらセキュリティも何もないよ....」という身もふたもない意見が飛んできそうですが、でもまったくそのとおりでしてそういった理由もあって簡単には出来ないということで納得もしていました。


ところが、先日ブログで「フォルダを監視したい」というバッチファイルに関する記事を書いた後に、これをちょっと直してサービスみたいな役割を持たせればリモート実行させるのに使えるかもと気付いたのです。
どういうことかというと、このバッチファイルでやっている「一定時間ごとにフォルダをチェックして、その状態を記録する」という機能はもう出来ていますので、これを「一定時間ごとにフォルダをチェックして、指定のバッチファイルがあったら実行する」というロジックに書きなおします。
そしてこのバッチファイルをタスクかスタートアップで常時実行しておけば、リモートから特定のフォルダにバッチファイルを入れるだけで、定期的に監視しているプロセスによってバッチファイルは起動されることになります。


バッチの起動アカウントも、タスクに設定する起動ユーザか、もしくはバッチをrunas経由で正しく指定すれば制御できます。


そんなわけでとりあえずサーバ側のバッチを作成してみました。


watchserver.cmd

@ECHO OFF
::***************************
:: 定数をセット
::***************************

:: ログファイル名
SET LOGFILE="%~dp0watchserver_%date:/=-%.log"

:: 実行するバッチファイル名
SET EXECBAT=Exec.bat

::***************************
:: ユーザからの指定値をセット
::***************************

:: チェックしたいフォルダ
SET CHECKDIR=%~dp0

:: チェックしたい間隔[秒]
SET CHECKINTERVAL=10

::***************************
:: ユーザからの指定値をセット
::***************************

:: 第一引数はチェックしたいフォルダを指定
IF %1. NEQ . (
  SET CHECKDIR=%1
)

:: 第二引数はチェックする間隔を指定
IF %2. NEQ . (
  SET CHECKINTERVAL=%2
)

::**********************************
:: とりあえずログファイルを初期化
::**********************************
ECHO +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > %LOGFILE%
ECHO ○ 処理開始日時:%DATE% - %TIME% >> %LOGFILE%
ECHO  作業者:%USERNAME%%USERDOMAIN% >> %LOGFILE%
ECHO. >> %LOGFILE%
ECHO  監視対象フォルダ:%CHECKDIR% >> %LOGFILE%
ECHO  監視間隔[秒]  :%CHECKINTERVAL%>> %LOGFILE%
ECHO +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >> %LOGFILE%
ECHO. >> %LOGFILE%

::**********************************
:: 値を確認して不足のある場合は終了
::**********************************
IF NOT EXIST %CHECKDIR% (
  ECHO ××××××××××××××××××××××××× >> %LOGFILE%
  ECHO 監視対象のフォルダ[%CHECKDIR%]は存在しません >> %LOGFILE%
  ECHO ××××××××××××××××××××××××× >> %LOGFILE%
  EXIT
)


:CHECKPROC
CLS
CALL :ECHO2 ========================================================
CALL :ECHO2 ○ [%CHECKDIR%] のチェック:%DATE% - %TIME%
CALL :ECHO2 ========================================================
ECHO.

:: 見つかった場合
IF EXIST %CHECKDIR%\%EXECBAT% (
  ECHO バッチファイル[%CHECKDIR%\%EXECBAT%]が見つかりました
  CALL %CHECKDIR%\%EXECBAT%"

  ECHO ○ 実行したバッチファイルは以下のファイルです
  ECHO -------------------------------------------------------------------- >> %LOGFILE%
  TYPE %CHECKDIR%\%EXECBAT% >> %LOGFILE%
  ECHO -------------------------------------------------------------------- >> %LOGFILE%
  ECHO. >> %LOGFILE%
  DEL %CHECKDIR%\%EXECBAT% /F /Q >> %LOGFILE%
  GOTO :PRWAIT
)

CALL :ECHO2 バッチファイルは見つかりませんでした
ECHO. >> %LOGFILE%

:PRWAIT
CALL :ECHO2 現在 [%TIME%] から%CHECKINTERVAL%秒間 待機します
PING -n %CHECKINTERVAL% 127.0.0.1 > NUL
GOTO CHECKPROC


:: 画面とログに両方出力する
:ECHO2
ECHO %*
ECHO %* >> %LOGFILE%
EXIT /B


このバッチファイルは第一引数にチェックするフォルダを、第二引数にチェック間隔(秒単位)を指定出来ます。
指定しない場合には、チェックするフォルダはバッチファイルと同じフォルダであり、チェック間隔は60秒です。
これを実行したら、あとは処理を実行したい内容を「Exec.bat」という名前のバッチファイルに記述してそれをリモートから指定のフォルダにコピーするだけです。


一点だけ注意しなければいけないのは、「Exec.bat」の起動方法についてです。
ここではCALLコマンドを使用していますが、この場合、もし呼び出されたバッチの中で「EXIT」コマンドが呼ばれるとこの呼び出し側のプロセス自身も一緒に終了してしまいます。そのため、Exec.batがEXITwoしないことがとても大事になります(どうしても書くのであれば EXIT /B と記述してください)。
これについては、「CALL」コマンド経由ではなく「START」コマンドでコマンドプロンプトを経由して起動すれば解決するのですが、そうすると今度は逆にEXITをしない場合にプロセスが終了せず、コマンドプロンプトが残ってしまうことになり、次のバッチ処理に移行しません。
この点だけは注意をしてください。


ということで、今回はかなり簡単な例を書きましたが、例えば指定フォルダ内のすべてのバッチファイルを実行する場合には

DIR /B *.bat *.cmd | FIND /V /I "%0"


の結果をforコマンドで利用することで実現できますし、改善の余地はたくさん残っています。このあたりはこれをベースに適当に作り直してフィードバックしていただけるととても嬉しいです。