発生している問題
あるバッチファイルから、パス名に括弧()
のあるバッチファイルをstart
できない。
例) test(hoge).bat
、foo(new)\update.bat
など。
実際のコード
解説するツリー構造は以下の通りです:
.
├── hoge.bat
└── test(hoge).bat
以下が期待通りに動作しないコードになります。
start "title" "test(hoge).bat"
pause
pause
hoge.batを開くと、新しいウィンドウは開くのですが、新しいウィンドウに次のような表示がされます。
'test' is not recognized as an internal or external command,
operable program or batch file.
原因
どうやらstart
コマンドは、.bat
ファイルや.cmd
ファイルを開くときに、
C:\WINDOWS\system32\cmd.exe
に/K
オプションと開きたいバッチファイルを引数として渡して開く挙動をするようです。
例えば、上のhoge.bat
なら、C:\WINDOWS\system32\cmd.exe /K "test(hoge).bat"
という風な実行のされ方をします。
ここで、cmd.exeは受け取った"test(hoge).bat"
の(
を特別な記号として解釈するので、結果的にtest
だけが実行されてしまって、上のようなエラーになるようです。
解決方法1 - あきらめる
パス名に括弧()
を使わないでください。 これが一番手っ取り早く、確実な方法です。
解決方法2 - callを活用する
start "title" "cmd.exe" /k call "test(hoge).bat"
と書くと実行できます。
原理としては、これまでの問題はcmd.exe
が(
を特別な記号として解釈していたからなのですが、なぜかcmd.exe
が受け取る第2引数以降は(
を普通の文字列として受け取るようです。それを利用して、start
先でcall
するという力業でどうにかなります。
解決方法3 - エスケープする
start "title" "cmd.exe" /k "test^^(hoge^^).bat"
と書くと実行できます。
この方法の肝は、キャレット^
を2回書くことです。こうすることで、呼び出すバッチ側が"test^(hoge^).bat"
と解釈して、呼び出された側のcmd.exe
で(
をエスケープできるので実行できます。
解決方法4 - 末尾に空白を付ける
start "title" "test(hoge).bat "
と書くと実行できます。
この方法の肝は、呼び出すパスの後に空白を付けることです。cmd.exe
は、なぜかパス名に空白があると、その文字列をパスとして認識し始めます。それを利用して、最後に空白を付けることで、パスとして認識させることで実行できます。
解決方法5 - ダブルクォーテーションを増やす
start "title" ""test(hoge).bat""
と書くと実行できます。
この方法の原理はわかりませんが、なぜか動きます。
この方法は、パス名に空白があると動かないので、使う際は注意して使ってください。
まとめ
cmd.exe、クソすぎる。
謝礼
質問に答えていただいた @imagou 様、 @HalHarada 様、 @mrbonjin 様ありがとうございました!