Y子です。
バッチファイルで使う変数のスコープ(有効範囲)が気になり、調べてみました。
今回は試行錯誤回です。
#概要
変数の値が、サブルーチンやsetlocal
でどのように変化するか、いろいろ組み合わせて確認します。
【確認環境:Windows 10 Home (Bld. 19042.928)】
#コード
##(1) サブルーチンのスコープ
サブルーチンの外で設定した値が、サブルーチンの中でどうなるか。
また逆に中で設定した値が外でどうなるか、の確認です。
@echo off
rem サブルーチンのスコープ
echo 1-a %var%
set var=aaa
echo 1-b %var%
call :subroutine
echo 3-a %var%
set var=ccc
echo 3-b %var%
exit /b
:subroutine
echo 2-a %var%
set var=bbb
echo 2-b %var%
exit /b
> scope_test1.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a bbb
3-b ccc
> scope_test1.bat
1-a ccc
1-b aaa
2-a aaa
2-b bbb
3-a bbb
3-b ccc
1-b
と2-a
が一致するので、サブルーチン外の値が中に持ち込まれるようです。
2-b
と3-a
が一致するので、サブルーチン内の値が外に持ち出されるようです。
つまり、サブルーチンの内外が、ひと続きの名前空間てことでしょうか。
なお、複数のバッチ間で同じ変数名を使うと、リセットされずに使いまわされてしまいます。
2回目の1-a
に、1回目の3-b
と同じ値が入っていることからわかります。
うっかりすると想定外の動作になるので、変数の初期化やローカル化が大切です。
##(2) setlocal/endlocalの効果確認
変数をローカル化させる「setlocal
」と、ローカル化を終わらせる「endlocal
」の効果を確認します。
@echo off
rem setlocal/endlocalの効果確認
echo 1-a %var%
set var=aaa
echo 1-b %var%
setlocal
echo 2-a %var%
set var=bbb
echo 2-b %var%
endlocal
echo 3-a %var%
set var=ccc
echo 3-b %var%
> scope_test2.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a aaa
3-b ccc
(1)と比べて、3-a
だけが変わりました。
1-b
と2-a
は一致するので、ローカル化前(setlocal
実行前)の値が持ち込まれています。
2-b
と3-a
は一致しません。2-b
はローカル化された内部で格納された値ですが、3-a
はローカル化前の値が復元された値です。
つまり、外部の値は持ち込まれるが、内部の値は持ち出せない、ということに見えます。
##(3) setlocal/endlocalをサブルーチンの外で使う
サブルーチンのコールの外を、setlocal
/endlocal
で囲ったらどうなるのか、確認します。
@echo off
rem setlocal/endlocalをサブルーチンの外で使う
echo 1-a %var%
set var=aaa
echo 1-b %var%
setlocal
call :subroutine
endlocal
echo 3-a %var%
set var=ccc
echo 3-b %var%
exit /b
:subroutine
echo 2-a %var%
set var=bbb
echo 2-b %var%
exit /b
> scope_test3.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a aaa
3-b ccc
(2)と同じ結果になりました。
サブルーチンの内外でスコープが変わらないと(1)で確認してありますので、サブルーチン以外の順番が一致する(2)と(3)は全く同じ処理になると理解できます。
##(4) setlocal/endlocalをサブルーチンの中で使う
サブルーチン内の最初と最後で、setlocal
/endlocal
を実行したらどうなるのか、確認します。
@echo off
rem setlocal/endlocalをサブルーチンの中で使う
echo 1-a %var%
set var=aaa
echo 1-b %var%
call :subroutine
echo 3-a %var%
set var=ccc
echo 3-b %var%
exit /b
:subroutine
setlocal
echo 2-a %var%
set var=bbb
echo 2-b %var%
endlocal
exit /b
> scope_test4.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a aaa
3-b ccc
これも(2)(3)と同じ結果になりました。
(3)と同様に、(2)と同じ順に処理が行われると理解できます。
##(5) setlocalは通らないと意味がない
念のための確認です。
setlocal
と書いてはあるが、よく見るとgoto
のせいで通っていないパターンです。
@echo off
rem setlocalは通らないと意味がない
echo 1-a %var%
set var=aaa
echo 1-b %var%
goto :label_a
setlocal
:label_a
echo 2-a %var%
set var=bbb
echo 2-b %var%
endlocal
echo 3-a %var%
set var=ccc
echo 3-b %var%
> scope_test5.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a bbb
3-b ccc
(1)と同じ結果になりました。
setlocal
を実行していないので、変数のローカル化が行われないという結果です。
まあ、当然すぎて笑っちゃいますが、確認は大事。
##(6) setlocalの替わりに遅延環境変数展開有効
(2)のsetlocal
の行を、環境変数を遅延展開する宣言であるsetlocal enabledelayedexpansion
にしたらどうなるか。
同じ「setlocal
」ですので、同じになるんじゃないですかね。どうでしょう。
@echo off
rem setlocalの替わりに遅延環境変数展開有効
echo 1-a %var%
set var=aaa
echo 1-b %var%
rem 遅延環境変数の展開を有効にする
setlocal enabledelayedexpansion
echo 2-a %var%
set var=bbb
echo 2-b %var%
endlocal
echo 3-a %var%
set var=ccc
echo 3-b %var%
> scope_test6.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a aaa
3-b ccc
(2)と同じ結果になりました。へー!
setlocal enabledelayedexpansion
後に格納した値は、ローカル化されてたんですね!
##(7) setlocal/endlocalの多重化
setlocal
の中で、さらにsetlocal
を実行したらどうなるのか!
ローカル化を2重掛けにしてみます。
@echo off
rem setlocal/endlocalの多重化
echo 1-a %var%
set var=aaa
echo 1-b %var%
setlocal
echo 2-a %var%
set var=bbb
echo 2-b %var%
setlocal
echo 3-a %var%
set var=ccc
echo 3-b %var%
endlocal
echo 4-a %var%
set var=ddd
echo 4-b %var%
endlocal
echo 5-a %var%
set var=eee
echo 5-b %var%
> scope_test7.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a bbb
3-b ccc
4-a bbb
4-b ddd
5-a aaa
5-b eee
3-b
と4-a
が一致しません。2回目のローカル化前の2-b
の値が復元されています。
4-b
と5-a
が一致せず、今度は1回目のローカル化前の1-b
の値が復元されています。
これはすごいですね!
多重化すると、同じ変数名であっても各階層の値が個別に維持されるようです。
##(8) setlocal/endlocal内の値を外側に渡す
新しい技です(わたし的には)。
endlocal && set
で、ローカル化された内部の値を持ち出せます。
@echo off
rem setlocal/endlocal内の値を外側に渡す
echo 1-a %var%
set var=aaa
echo 1-b %var%
setlocal
echo 2-a %var%
set var=bbb
echo 2-b %var%
endlocal && set var=%var%
echo 3-a %var%
set var=ccc
echo 3-b %var%
> scope_test8.bat
1-a
1-b aaa
2-a aaa
2-b bbb
3-a bbb
3-b ccc
(1)や(5)と同じ結果ですが、これはローカル化が行われなかったわけではありません。
ローカル化されたうえで、内部の値が外に持ち出された結果です。
関数の戻り値みたいですね。これは使えそう。
#おわりに
いろいろ試しました。
これが何に使えるかはまだ考えてませんが、中の値を外に持ち出して…のような関係がちょっとスッキリしました。
まだ他にもやり方があるんでしょうけどねー。
今後につながる気がします!
では!
#参考
勉強させていただきました。