tech.guitarrapc.cóm

Technical updates

Window バッチファイルでパラメーター入力を受け付ける

Windows バッチファイル (.bat や .cmd) を実行するときに、パラメーターを指定して引数を渡したいと思ったのでおいておきます。 これまで面倒で考えてなかったのですが、便利。

tl;dr;

  • シェルスクリプトと同じように引数を組めるので、Windowsバッチだからパラメーター入力できないとあきらめる必要はありません。(過去の自分へ)
  • 複数のパラメーターをユーザーが選択して指定できるようにしたい、こういったときにパラメーター入力は便利です
  • 引数解析も単純なので定番として利用できます

gist においておきます。

https://gist.github.com/guitarrapc/a6c3fc433e7f98ef74f6cb45f4f2a952

どういうことをしたいのか

イメージしやすいように シェルスクリプト (Bash) や PowerShell で考えてみましょう。

シェルスクリプトを利用するときに、パラメーター名 値 で引数を渡すことがよくあります。

# X: 引数の順序に依存する
./foo.sh true true 1

# O: パラメーター名で指定する
./foo.sh --foo --bar --param 1

PowerShell でスクリプトを書いた時も、Param() を使って標準的に行われるパターンです。

# O: パラメーター名で指定する
./foo.ps1 -Foo -Bar -Param 1

Windows バッチでもシェルスクリプトのように、パラメーターで引数を指定したいですね。

foo.bat --foo --bar --param 1

パラメーターで引数を渡すメリットは、引数が順序に縛られずパラメーター名さえわかっていれば任意の順序で指定できることです。スクリプトを利用するときに圧倒的に楽です。

Windows バッチファイルでパラメーターを指定して引数を渡す

次のようなシェルスクリプトがあったとして..。

#!/bin/bash
set -euo pipefail

while [ $# -gt 0 ]; do
    case $1 in
        --foo) _FOO=true; shift 1; ;;
        --bar) _BAR=true; shift 1; ;;
        --param) _PARAM=$2; shift 2; ;;
        --param2) _PARAM2=$2; shift 2; ;;
        *) shift ;;
    esac
done

if [[ "${_FOO:=""}" == "true" ]]; then echo "--foo detected"; fi
if [[ "${_BAR=""}" == "true" ]]; then echo "--bar detected"; fi
if [[ "${_PARAM:=""}" != "" ]]; then echo "--param ${_PARAM}"; fi
if [[ "${_PARAM2:=""}" != "" ]]; then echo "--param2 ${_PARAM2}"; fi

これに相当するWindows バッチファイルでの書き方の例を示します。

@echo off

setlocal
:parse
    if "%~1"=="" GOTO endparse
    if "%~1"=="--foo" (set _FOO=true)
    if "%~1"=="--bar" (set _BAR=true)
    if "%~1"=="--param" (set _PARAM=%~2)
    if "%~1"=="--param2" (set _PARAM2=%~2)
    shift
    GOTO parse
:endparse

if "%_FOO%"=="true" (echo --foo detected)
if "%_BAR%"=="true" (echo --bar detected)
if not "%_PARAM%"=="" (echo --param %_PARAM%)
if not "%_PARAM2%"=="" (echo --param2 %_PARAM2%)

endlocal

想定通り、任意のパラメーターを指定できるのがわかります。

> parse.bat

> parse.bat --foo --bar
--foo detected
--bar detected

> parse.bat --foo --bar --param 1
--foo detected
--bar detected
--param 1

> parse.bat --foo --bar --param2 5
--foo detected
--bar detected
--param2 5

> parse.bat --foo --bar --param2 5 --param 100
--foo detected
--bar detected
--param 100
--param2 5

> parse.bat --foo --bar --param 1 --param2 5
--foo detected
--bar detected
--param 1
--param2 5

簡単な説明

:parse から :endparse が引数の指定です。

Windows バッチに while 構文がないので、 goto で代用しています。引数解析は、shift で引数をずらしながら、空になるまでgoto でループするだけです。 setlocalendlocal でスクリプト内の変数を外部出さないようにすると、実行シェルに影響与えないのでお行儀がいいです。

まとめ

Windows バッチファイルも便利。