6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

バッチファイルの変数のスコープ(試行錯誤)

Last updated at Posted at 2021-04-20

Y子です。
バッチファイルで使う変数のスコープ(有効範囲)が気になり、調べてみました。
今回は試行錯誤回です。

#概要
変数の値が、サブルーチンやsetlocalでどのように変化するか、いろいろ組み合わせて確認します。
確認環境:Windows 10 Home (Bld. 19042.928)

#コード
##(1) サブルーチンのスコープ
サブルーチンの外で設定した値が、サブルーチンの中でどうなるか。
また逆に中で設定した値が外でどうなるか、の確認です。

scope_test1.bat
@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-b2-aが一致するので、サブルーチン外の値が中に持ち込まれるようです。
2-b3-aが一致するので、サブルーチン内の値が外に持ち出されるようです。
つまり、サブルーチンの内外が、ひと続きの名前空間てことでしょうか。

なお、複数のバッチ間で同じ変数名を使うと、リセットされずに使いまわされてしまいます。
2回目の1-aに、1回目の3-bと同じ値が入っていることからわかります。
うっかりすると想定外の動作になるので、変数の初期化やローカル化が大切です。

##(2) setlocal/endlocalの効果確認
変数をローカル化させる「setlocal」と、ローカル化を終わらせる「endlocal」の効果を確認します。

scope_test2.bat
@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-b2-aは一致するので、ローカル化前(setlocal実行前)の値が持ち込まれています。
2-b3-aは一致しません。2-bはローカル化された内部で格納された値ですが、3-aはローカル化前の値が復元された値です。
つまり、外部の値は持ち込まれるが、内部の値は持ち出せない、ということに見えます。

##(3) setlocal/endlocalをサブルーチンの外で使う
サブルーチンのコールの外を、setlocal/endlocalで囲ったらどうなるのか、確認します。

scope_test3.bat
@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を実行したらどうなるのか、確認します。

scope_test4.bat
@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のせいで通っていないパターンです。

scope_test5.bat
@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」ですので、同じになるんじゃないですかね。どうでしょう。

scope_test6.bat
@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重掛けにしてみます。

scope_test7.bat
@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-b4-aが一致しません。2回目のローカル化前の2-bの値が復元されています。
4-b5-aが一致せず、今度は1回目のローカル化前の1-bの値が復元されています。
これはすごいですね!
多重化すると、同じ変数名であっても各階層の値が個別に維持されるようです。

##(8) setlocal/endlocal内の値を外側に渡す
新しい技です(わたし的には)。
endlocal && setで、ローカル化された内部の値を持ち出せます。

scope_test8.bat
@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)と同じ結果ですが、これはローカル化が行われなかったわけではありません。
ローカル化されたうえで、内部の値が外に持ち出された結果です。
関数の戻り値みたいですね。これは使えそう。

#おわりに
いろいろ試しました。
これが何に使えるかはまだ考えてませんが、中の値を外に持ち出して…のような関係がちょっとスッキリしました。
まだ他にもやり方があるんでしょうけどねー。

今後につながる気がします!

では!

#参考
勉強させていただきました。

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?