前期はcallを使用したバッチの関数化について紹介しました。
今回の記事では関数化した処理の中で変数を使用する場合の注意点について紹介します。
変数のスコープ
バッチは普通に書くと変数のスコープがないのですべての変数がグローバル変数のような動きをします。
そのため同じ変数名を使うと常に内容が上書きされます。以下で簡単な例を示します。内容はname変数を定義した後、別の内容に更新してどのように変化するか表示するものです。
batch_function03.cmd
set name=Isaac Newton
echo Display Name01: %name%
set name=Leonhard Euler
echo Display Name02: %name%
echo Display Name03: %name%
このバッチを実行すると以下の結果となります。
batch_function03.cmd実行結果
Display Name01: Isaac Newton
Display Name02: Leonhard Euler
Display Name03: Leonhard Euler
当たり前の結果ですが、最初「Isaac Newtown」で定義されたname変数が「Lenhard Euler」に更新されたため3回目の表示は「Lenhard Euler」になっています。
上記のような単純な例では特に問題になる事はありませんが、バッチを関数化して使い回しをすると変数名が衝突し正常に処理ができないケースが出てきます。
この問題に対応するためにバッチでローカル変数を使う方法を紹介します。
変数のローカル化
前述した通りバッチで普通に変数定義をするとグローバル変数になってしまいますが、「setlocal」〜「endlocal」の間で定義した変数はローカル変数になります。
最初に紹介したバッチに少して手を加えてみます。
batch_function04.cmd
set name=Isaac Newton
echo Display Name01: %name%
setlocal
set name=Leonhard Euler
echo Display Name02: %name%
endlocal
echo Display Name03: %name%
これを実行すると下記のような結果となります。
batch_function04.cmd実行結果
Display Name01: Isaac Newton
Display Name02: Leonhard Euler
Display Name03: Isaac Newton
このように「setlocal」の後にname変数を上書きしていますが、「endlocal」の後name変数の値には影響していない事が分かります。
関数内で変数を使用する例
これまで紹介したテクニックを使って関数内で変数を使用する例を紹介します。
batch_function05.cmd
@echo off
rem ########################################################
rem # main関数呼び出し
rem ########################################################
call :main
goto :eof
rem ########################################################
rem # 関数定義
rem ########################################################
:sum
setlocal
echo sum start
call :sum_sub %*%
echo sum end
endlocal
exit /b %errorlevel%
goto :eof
:sum_sub
echo sum_sub start
set op1=%~1%
set op2=%~2%
set /a total=%op1% + %op2%
echo sum_sub end
exit /b %total%
goto :eof
rem ########################################################
rem # main関数定義
rem ########################################################
:main
echo main start
rem # 変数設定
set a=10
set b=5
set c=1
set d=2
echo a=%a%
echo b=%b%
echo c=%c%
echo d=%d%
rem # main内計算結果表示
set /a total=%c% + %d%
echo 01:main total=%total%
rem # 関数呼び出し
call :sum %a% %b%
set sum_total=%errorlevel%
rem # 関数呼び出し結果表示
echo 02:sum sum_total=%sum_total%
rem # main内計算結再果表示
echo 03:main total=%total%
echo main end
exit /b 0
goto :eof
実行すると下記のような結果となります。
batch_function05.cmd実行結果
main start
a=10
b=5
c=1
d=2
01:main total=3
sum start
sum_sub start
sum_sub end
sum end
02:sum sum_total=15
03:main total=3
main end
main関数とsum_sub関数の両方でtotal変数を使用していますがmain関数のtotalが書き換わっていない事に注意してください。
このように「setlocal」と「endlocal」で囲むことによってローカル変数が使用できるので変数名の衝突を気にする事なく関数がかけるので一度書いた関数を別のバッチに流用する事が容易になります。
補足
変数名の衝突を避ける方法としては「setlocal」と「endlocal」を使う方法以外にも必ず変数名が重ならないように定義する方法もあります。
例えば変数名の命名規則を必ず関数名+変数名にする等の方法です。上のバッチの例ではmain関数のtotalをmain_totalとし、sum_sub関数のtotalをsum_sub_totalのようにする方法です。
この方法でも全く問題ありませんが、変数名が長くなりがちになるのでバッチが見にくくなる事があります。必要に応じて使い分けて頂ければと思います。
参考までに変数名を工夫する方法で書いたバッチをサンプルバッチのリンク先に載せておきます。ファイル名は「batch_function05-01.cmd」です。
より実用的な例
次回の記事でここでご紹介したテクニックを使ってより実用的な例をお見せいたしますので気になる方はまたブログを見に来ていただければと思います。
サンプルバッチ
前回同様紹介したバッチをGitHubにアップロードしているので気になる方はチェックしてみて下さい。