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ファイルを作成する際は,この方法でヘルプを実装するようにしたい。
