HOME > PowerShell > チュートリアル > 変数のスコープ

変数のスコープ


作成日 2018年06月04日
更新日 2018年06月06日
【外部リンク】変数のスコープはマイクロソフトの「about_Scopes」を参照しました。
int、double、stringなどの基本的な型の変数のスコープを説明します。(配列、連想配列、クラスなどを除く)

function、filter、名前のない関数のスクリプト ブロックは、{ と } で囲みます。
if、for、foreach、while、switchなどの { と } はステートメント ブロックといいスクリプト ブロックとは違います。
スクリプト ブロックによりスコープが変わります。(ステートメント ブロックでは変わりません)
PowerShellのスコープは多くのプログラマーが想像するグローバル、プライベートとは異なるでしょう。

Privateスコープ以外は、ラベルなしで親の変数の値を取得できます。
AllScopeスコープ以外は、親の変数の値に設定するにはラベルが必要です。ラベルなしは親と同じ名前の変数を新規作成します。親の変数の値は変わりません。
  1. AllScopeはどこからでも値を取得、設定できます。(非推奨)
  2. 「Global:」でグローバルに変数を設定する
  3. 「Script:」でスクリプト ファイルの変数を設定する
  4. 番号スコープで値を取得する
  5. ドット ソース形式で読み込んだPrivateスコープ
  6. ドット ソース形式で読み込んだ設定変数のスコープ
  7. 配列、連想配列は子のスクリプト ブロックで設定できる

AllScopeはどこからでも値を取得、設定できます。(非推奨)

AllScopeはラベルなしでどこからでも値を取得、設定できます。
すべてのスクリプト ファイル、子のスクリプト ブロックでも値を設定できます。
名前からグローバル変数か識別できないので推奨できません。業務ではGlobalスコープを使用してください。
New-Variableコマンドレットの、-Optionパラメーターに「AllScope」を指定します。
-Nameパラメーターに変数名を、-Valueパラメーターに値を指定します。
下記は名前が「as_MinValue」、値が「100」、スコープがAllScopeの変数を作成します。
PS > New-Variable -Name as_MinValue -Value 100 -Option AllScope

下記のようなスクリプト ファイルの「.\a.ps1」を作成します。
$as_MinValue += 1
$as_MinValue
&{
    $as_MinValue += 1
    $as_MinValue
}

「.\a.ps1」を実行すると子のスクリプト ブロックからも、スコープのラベルなしで変数の値を設定します。
PS > .\a.ps1
101
102

PS >  .\a.ps1
103
104
このページの上

「Global:」でグローバルに変数を設定する

コマンドラインのスクリプト ブロックの中以外の変数で、ラベルなしはGlobalスコープになります。
プロファイルの変数と関数のスコープはグローバルです。
ドット ソース形式で読み込むと、スクリプト ブロックの中以外はグローバルになります。
Windows PowerShell ISEの「デバッグ」の「実行/続行」メニューは、スクリプト ブロックの中以外はグローバルになります。
Localスコープを説明します。
Privateスコープ以外は、ラベルなしで親の変数の値を取得できます。
AllScopeスコープ以外は、親の変数の値に設定するにはラベルが必要です。ラベルなしは親と同じ名前の変数を新規作成します。親の変数の値は変わりません。
PS > $g = 0  # Globalスコープ
PS > &{ $g; $g = 1; $g }  # Globalスコープの$gを表示し、Localスコープの$gに値を設定して表示します。
0
1
PS > $g  # Localスコープの値を設定しても、Globalスコープの値は変更しません。
0
子のスクリプト ブロックで同じ名前の変数に値を代入しても、ルートの変数の値を変更できませんでした。

グローバルに値を設定できるように、変数の名前の前にラベルの「Global:」を付けます。
PS > $g = 0  # Globalスコープ
PS > &{ $g; $Global:g = 1; $Global:g }  # $Global:gは、Globalスコープです。
0
1
PS > $g
1
スクリプト ファイル内でも「Global:」ラベルを付けると、そのファイルの外の値を設定できます。
グローバル変数の名前は重複しないように長い名前にしたり接頭語を付けましょう。
このページの上

「Script:」でスクリプト ファイルの変数を設定する

スクリプト ファイルのスクリプト ブロックの中以外で定義したラベルなしの変数はScriptスコープになります。
Localスコープを説明します。
Privateスコープ以外は、ラベルなしで親の変数の値を取得できます。
AllScopeスコープ以外は、親の変数の値に設定するにはラベルが必要です。ラベルなしは親と同じ名前の変数を新規作成します。親の変数の値は変わりません。
よってScriptスコープはコマンドラインや、他のスクリプト ファイルから参照できません。
$a = 0
&{
    $a     # 0 ラベルなしで親の変数の値を取得できます。
    $a = 1 # 1 ラベルがないので新しく作成した変数に代入します。
    $a     # 1 ラベルがないので上の行と同じ変数です。
}
$a # 0 のままで変わりません。
実行してもルートの階層の変数の値は変わりません。
0
1
0

同じスクリプト ファイルで値を設定できるように、変数の名前の前にラベルの「Script:」を付けます。
$a = 0
&{
    $a # 0
    $Script:a = 1 # 1 スクリプトファイルの変数に代入します。
    $Script:a     # 1 
}
$a # 1 に変わります。
実行するとルートの階層の変数の値が、1に変わりました。
0
1
1
このページの上

番号スコープで値を取得する

Get-Variableコマンドレットの-Scopeパラメーターに、相対的な位置の数値を指定して変数の値を取得します。
Scopeの数値の0は同じ階層、1は親、2は親の親のスクリプト ブロックです。
$Location = "親"
&{
    $Location = "子"
    &{
        $Location = "孫"
        
        Write-Host ("Location 2 " + (Get-Variable -Name Location -Scope 2 -ValueOnly))
        Write-Host ("Location 1 " + (Get-Variable -Name Location -Scope 1 -ValueOnly))
        Write-Host ("Location 0 " + (Get-Variable -Name Location -Scope 0 -ValueOnly))
    }
}
Location 2 親
Location 1 子
Location 0 孫
このページの上

ドット ソース形式で読み込んだPrivateスコープ

下記のファイルの名前を「.\a.ps1」にして保存します。
$Private:p = "Private:p"
Write-Host $Private:p
&{ $Private:child = "Private:child"; $Private:child }

ドット ソース形式とはファイル名の前に半角のドット記号とスペースを入力して実行します。ファイル名が「.\a.ps1」は、「. .\a.ps1」で実行します。
ドット ソース形式で読み込むと、スクリプト ブロックの中以外は「Private:」変数でもグローバルになります。
あるいはWindows PowerShell ISEの「デバッグ」の「実行/続行」メニューを実行した場合です。
PS > . .\a.ps1
Private:p
Private:child

PS > $p
Private:p

PS > $child
変数 '$child' は、設定されていないために取得できません。
発生場所 行:1 文字:1
+ $child
+ ~~~~~~
    + CategoryInfo          : InvalidOperation: (child:String) []、ParentContainsErrorRecordException
    + FullyQualifiedErrorId : VariableIsUndefined
コマンドラインから変数の$pの値を取得、設定できるようになりました。
このページの上

ドット ソース形式で読み込んだ設定変数のスコープ

ドット ソース形式で読み込んだ設定変数は、スクリプト ブロックの中はプライベートです。
スクリプト ブロックの外はグローバルで、値を変更すると変化したままになります。

下記をスクリプト ファイルの名前を「.\a.ps1」にして保存します。
$DebugPreference = "Stop"
$DebugPreference 
「.\a.ps1」で実行すると、コマンドラインで「$DebugPreference」の値は変化しません。
PS > $DebugPreference = "Continue"

PS > .\a.ps1
Stop

PS > $DebugPreference
Continue

ファイル名の前にドットとスペースを付けて「. .\a.ps1」で実行します。
ドット ソースで実行すると、コマンドラインで「$DebugPreference」の値は変化したままです。
PS > . .\a.ps1
Stop

PS > $DebugPreference
Stop

次は子のスクリプト ブロックで設定変数を変更します。
下記をスクリプト ファイルの名前を「.\a.ps1」にして保存します。
&{
    $DebugPreference = "Stop"
    $DebugPreference 
}
「.\a.ps1」で実行すると、コマンドラインで「$DebugPreference」の値は変化しませんでした。
PS > $DebugPreference = "Continue"

PS > .\a.ps1
Stop

PS > $DebugPreference
Continue

ファイル名の前にドットとスペースを付けて「. .\a.ps1」で実行します。
ドット ソースで実行しても、コマンドラインで「$DebugPreference」の値は変化しませんでした。
PS > $DebugPreference = "Continue"

PS > . .\a.ps1
Stop

PS > $DebugPreference
Continue
このページの上

配列、連想配列は子のスクリプト ブロックで設定できる

配列は子のスクリプト ブロックで項目の値を設定できます。
連想配列は子のスクリプト ブロックで値を追加、削除、変更できます。

スクリプト ブロックの中で親の配列の項目の値を設定できます。
function Write-Array {
    Write-Output $arr
    Write-Output ("-" * 12)
    $arr[0] = "変わります。"
    Write-Output $arr
}

$arr = 1,2
Write-Array
Write-Output ("-" * 12)
Write-Output $arr
関数の中で$arr[0]の値を変更すると、親の配列の値も変わりました。
1
2
------------
変わります。
2
------------
変わります。
2
【注意】配列の足し算とかは、代入するための配列を新規作成します。その場合は名前が同じでも違う変数です。次の左側の$arrは別の変数です。 $arr = $arr + 3

次は子のスクリプト ブロックで連想配列の値を追加、削除、変更します。
$hash = @{ key1="Apple"; key2="Banana" }
&{
    Write-Output $hash
    Write-Output ("-" * 41)
    $hash["key1"] = "Strawberry"
    $hash.Remove("key2")
    $hash.add("key3", "Orange") 
    Write-Output $hash
}
Write-Output ("-" * 41)
Write-Output $hash
子のスクリプト ブロックで変更したままです。
Name                           Value
----                           -----
key1                           Apple
key2                           Banana
-----------------------------------------
key3                           Orange
key1                           Strawberry
-----------------------------------------
key3                           Orange
key1                           Strawberry

次は子のスクリプト ブロックでクラスの値を変更します。(classはPowerShell 5以降)
class myLabel { 
    [string]$Label

    myLabel([string]$Label)
    {
        $this.Label = $Label
    }

    Show() {
        Write-Host ("Label = " + $this.Label)
    }
}

$lbl = [myLabel]::new("Name:")
&{
    $lbl.Show()
    $lbl.label = "Adress:"
    $lbl.Show()
}
$lbl.Show()
子のスクリプト ブロックで変更したままです。
Label = Name:
Label = Adress:
Label = Adress:
列挙型のenumの変数は子のスクリプト ブロックで変更しても、親では変わりませんでした。
このページの上