Knowlbo 開発者ブログ

株式会社Knowlboの開発者ブログです。

PowerShell スクリプトで気をつけたいこと その2

開発部の本橋です。先日投稿した PowerShell スクリプトで気をつけたいこと の続編です。

親スコープの変数を子スコープのブロックから書き換えたい

スクリプト言語にはよくある話ですが、親スコープに存在する変数を書き換える際に気をつける必要があります。

そもそも PowerShell のスコープには次の5種類があります。*1

  • Global
    • その名の通りグローバルスコープです。スクリプトファイル外からでも参照可能です。
  • Local
    • カレントのスコープです。子スコープからも参照可能です。
  • Script
    • 同一スクリプトファイルの中でのみ参照可能です。
  • Private
    • カレントスコープでのみ参照可能です。
  • Numbered Scopes
    • カレントブロックからの相対数を指定してスコープを参照します。

何も考えずに変数を定義すると Local スコープになります。以下はうまくいかない例です。

# このスコープに変数 $a を定義 (Local スコープ)
$a = 'A'

# スクリプトブロックを定義して子スコープを作る
$fn = {
    Write-Host $a # ==> A

    # 親スコープの変数 $a を書き換えたつもりだが、
    # このスコープに変数 $a を定義 (Local スコープ)
    $a = 'B'

    Write-Host $a # ==> B
}

# スクリプトブロックを呼び出し
& $fn

# このスコープの $a は書き換わっていない
Write-Host $a # ==> A

$a をスクリプトブロックの中で書き換えたつもりでも、スクリプトブロックを抜けると $a はもとに戻ってしまいます。コメントにも書きましたが、$fnLocal スコープに $a を定義してしまっているためです。

この場合、スコープを指定して $a に代入します。

# このスコープに変数 $a を定義 (Local スコープ)
$a = 'A'

# スクリプトブロックを定義して子スコープを作る
$fn = {
    Write-Host $a # ==> A

    # 1つ上のスコープを指定して変数 $a を書き換える
    Set-Valiable -Name a -Scope 1 -Value 'B'

    # もしくは Script スコープの変数 $a を定義する
    #$script:a = 'B'

    Write-Host $a # ==> B
}

# スクリプトブロックを呼び出し
& $fn

# $a は書き換わっている
Write-Host $a # ==> B

Set-Variable コマンドレットで一つ上のスコープを指定して変数の値を書き換えてあげればうまくいきます。また、上記コメント中に書いたように $script:a = 'B'とすると Script スコープの変数に代入する形になります。意図しない上書きをしてしまう場合があるので気をつけましょう。