初投稿です。
タイトル付けるの難しい...
やりたいこと
- sshで、Windows上のバッチファイル(親バッチファイル)をリモート実行
- 親バッチファイル内部で、startコマンドを使って非同期で子バッチファイルを呼び出す
- 子バッチファイルの終了を待たずに、親バッチファイルを終了させたい
実装NG例
@echo off
echo "%time% test1.bat start." > C:\work\test.log
rem 10秒スリープ
ping -n 11 127.0.0.1 > nul
rem test2.batを非同期実行
start "" C:\work\test2.bat
rem 5秒スリープ
ping -n 6 127.0.0.1 > nul
echo "%time% test1.bat end." >> C:\work\test.log
exit /b 0
@echo off
echo "%time% test2.bat start." >> C:\work\test.log
rem 時間がかかる処理
ping -n 31 127.0.0.1 > nul
echo "%time% test2.bat end." >> C:\work\test.log
exit 0
test1.batを直接実行したときのログがこちら:
"13:12:55.37 test1.bat start."
"13:13:05.69 test2.bat start."
"13:13:10.70 test1.bat end."
"13:13:36.12 test2.bat end."
test1.batを起動して10秒後にtest2.batが非同期で起動して
その5秒後にtest1.batは終了、非同期起動の30秒後にtest2.batも終了した。
次に、test1.batをSSH越しに実行したときのログがこちら:
"13:17:21.53 test1.bat start."
"13:17:31.78 test2.bat start."
"13:17:36.81 test1.bat end."
あれ? test2.batの終了ログが出ていない?
原因
タスクマネージャーを見ると原因がわかる。
SSH越しにtest1.batを実行したときの、タスクマネージャーの表示を見てみる。
- test1.batを起動した後
SSH実行により、PID=3936のconhost.exeが起動。
PID=13556のcmd.exeがtest1.batを実行している。
conhost.exe、cmd.exeともに「ジョブオブジェクトID=1000」の中で実行している(画面最右列)。
- test2.batが非同期実行された後
PID=12464のcmd.exeが立ち上がり、test2.batを実行している。
このcmd.exeも、「ジョブオブジェクトID=1000」の中で実行している。
- sshコマンドの応答が返ってきた後
ジョブオブジェクトID=1000のプロセスがすべて消滅した。
※ちなみに、test1.batを直接実行したときはジョブオブジェクトIDでまとめられていなかった。
対策
明示的に「違うジョブオブジェクトIDで起動して」といえばよいのだが、start
コマンドにはそのようなオプションがないみたい。
そのため、Win32_Process
クラスのCreate
メソッドでtest2.batが動くプロセスを作るようにした。
@echo off
echo "%time% test1-2.bat start." > C:\work\test.log
ping -n 11 127.0.0.1 > nul
rem test2.batを非同期実行
powershell -Command "Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine='C:\work\test2.bat'}"
ping -n 6 127.0.0.1 > nul
echo "%time% test1-2.bat end." >> C:\work\test.log
exit /b 0
念のため、test1-2.batを直接実行したときのログがこちら:
"13:36:58.41 test1-2.bat start."
"13:37:09.88 test2.bat start."
"13:37:14.94 test1-2.bat end."
"13:37:40.22 test2.bat end."
そして、test1-2.batをSSH越しに実行したときのログがこちら:
"13:39:40.37 test1-2.bat start."
"13:39:51.46 test2.bat start."
"13:39:56.52 test1-2.bat end."
"13:40:21.79 test2.bat end."
ちゃんとtest2.batが完走した。
タスクマネージャを見てみよう。
- test1.batを起動した後
SSH実行により、PID=7948のconhost.exeが起動。
PID=8268のcmd.exeがtest1-2.batを実行している。
conhost.exe、cmd.exeともに「ジョブオブジェクトID=744」の中で実行している(画面最右列)。
- test2.batが非同期実行された後
PID=12260のcmd.exeが立ち上がり、test2.batを実行している。
このcmd.exeは、「ジョブオブジェクトID=744」に属していない。
- sshコマンドの応答が返ってきた後
ジョブオブジェクトID=744のプロセスがすべて消滅したが、PID=12260のcmd.exeは動き続けている。
参考
ジョブオブジェクト を使用すると、プロセスのグループを1つの単位として管理できます。
(中略)
たとえば、作業セットのサイズやプロセスの優先順位などの制限の適用や、ジョブに関連付けられているすべてのプロセスの終了などがあります。
we currently achieve this through some P/Invoke calls in PoweShell and call the CreateProcess Win32 API with a specific flag.
OpenSSHがWindowsに移植されてから、いろんな人がこの問題に直面している模様。
この回答を見ると、Win32_Process Create
するときにCREATE_BREAKAWAY_FROM_JOB
を指定してあげた方がいいのかも。