cmd.exeでの/?オプションの実装方法
概要
コマンドプロンプト (cmd.exe
) で簡単なコマンドファイル (.cmd)やバッチファイル (.bat) を作っている。せっかくだから,オプションとヘルプも用意しようとしている。
Windowsの多くのコマンドは,オプションに/?
を指定すると,ヘルプが表示されるようになっている。これを自力で実装しようとしたところ,思っていた以上に手こずってしまった。
検討の末いい案が思いついたので,そのことを記す。
結論としては,以下のように無害なbreak
コマンドに全引数を渡して,/?
が含まれるとコマンドが失敗するのでそのタイミングでメッセージを表示する。
:: \file arg.cmd
break %* >nul || (
echo help
exit /b 1
)
echo arg: %*
以下でこの方法を見つけるまでの検討内容を記す。なお,こちらの例はあとあと参照しやすいように「example/help-implementation.cmd at master · senooken/example」にも掲載している。
方法
cmd.exe
の引数は%*
に全て入っている。ここから/?を識別できれば,それで事足りる。しかし,これが思っていた以上に難しかった。
5種類の方法を試したので,それぞれの検討内容について記す。
1. for
コマンドでオプションを解析しようとすると,?
が空白とみなされて,識別できない。ワイルドカードとみなされて展開されるようだ。
:: forでやる場合、?が空白とみなされるのでうまくいかない
:: for /f "delims= " %%A in ("%*") do echo "%%A"
for %%A in (%*) do echo "%%A"
2. 愚直に引数を1個ずつ%1 == /?
などでチェックするという方法がある。こちらはやり方が汚いし,10個目以上の引数を識別できない。
3. findstr
で%*
から/?
を識別する。この方法だと,/?が””や”で囲まれてエスケープされている場合の扱いが面倒くさくなる。
:: findで' /?'を探す。ただし、""か''で囲まれてエスケープされている場合をどうするか
:: findstrだと"/?"と/?を区別できない
echo "%*"|findstr \^"/?\^"
echo find help? %errorlevel% %*
echo "%*"|findstr '.*/?.*'
echo find help? %errorlevel% %*
:: 以下のパターンを認識できればいいか
:: command /? "/?" '/?'
4. call
に引数を渡して、関数内のshift
でif "%1" == "/?"
でチェックする。ただし,これだとどうしても"/?"
を/?
と誤認してcall
のヘルプが表示される。
call :args %*
:args
echo call
echo args: %*: 1:%1 2:%2 3:%3 4:%4
echo help!
if "%2" == "" echo 2 empty
if "%3" == "" echo 3 empty
if "%4" == "" echo 4 empty
if "%5" == "" echo 5 empty
exit /b 0
5. cmd.exe
の通常のコマンドの引数に/?
が渡されたらstatusが1
で失敗となることを利用する。無害な既存コマンドに%*
をそのまま渡して,/?
を認識したらstatus 1になることで識別する。
この用途で使えそうなコマンドは、break, call, echo, setlocal, rem
となる。この内,echo
とrem
は/?
を識別できない。
setlocalは対話シェルだと問題ないが,cmdファイルで任意の引数を指定すると,コマンドに無効なパラメーターが指定されましたと問答無用でエラーになる。
同じくcall
コマンドも関数以外の任意引数が渡されるとエラーになる。
消去法で残ったのがbreak
となる。多くのプログラミング言語でのbreak
はforやwhileの反復を中断するという意味だが,cmd.exe
のbreak
には一切そういう意味がない。
参考までにbreak
のヘルプを以下に示す。
break /?
DOS システム上で Ctrl + C キーの拡張チェック機能を設定または解除します。 この機能は DOS システムとの互換性を維持するために用意されています。Windows 上 では何も効果はありません。 Windows プラットフォームでコマンド拡張機能を有効にして実行中の場合、 デバッガーによるデバッグ時に BREAK コマンドはハード コード ブレークポイント を入力します。
ここに記載されている通り,DOSシステムとの互換性を維持するためのコマンドであり,Windows上では何の効果もない。
したがって,/?
の解析用コマンドとしてうってつけだ。
これを利用して,以下のようにbreak
の引数に%*
をそのまま指定し,標準出力は>nul
で捨てる。
break %* >nul && echo ok || echo ng
これにより,引数%*
に/?
が指定されている場合だけ,コマンドが失敗して||
以降が実行される。
これを利用して,実際にはcmdファイルの冒頭に以下のような/?
のチェック処理を施して,ヘルプメッセージを表示させればよい。
:: \file arg.cmd
break %* >nul || (
echo help
exit /b 1
)
echo arg: %*
cmdファイルでの/?
の判定・実装はこれで実現できるだろう。
結論
コマンドプロンプト (cmd.exe
) のコマンドファイル (.cmd) やバッチファイル (.bat) でのオプション (/?
) の識別・実装方法を記した。
制限の多いcmdファイルを作成することはあまりないだろうが,しっかり作り込んだ重要なコマンドなどにはきちんとヘルプも実装したほうがあとあといい。
かなりニッチな内容となるが,今後cmdファイルを作成する際は,この方法でヘルプを実装するようにしたい。