HOME > PowerShell > チュートリアル > 変数のスコープ
変数のスコープ
作成日 2018年06月04日
更新日 2018年06月06日
【外部リンク】変数のスコープはマイクロソフトの「about_Scopes」を参照しました。
int、double、stringなどの基本的な型の変数のスコープを説明します。(配列、連想配列、クラスなどを除く)
function、filter、名前のない関数のスクリプト ブロックは、{ と } で囲みます。
if、for、foreach、while、switchなどの { と } はステートメント ブロックといいスクリプト ブロックとは違います。
スクリプト ブロックによりスコープが変わります。(ステートメント ブロックでは変わりません)
PowerShellのスコープは多くのプログラマーが想像するグローバル、プライベートとは異なるでしょう。
Privateスコープ以外は、ラベルなしで親の変数の値を取得できます。
AllScopeスコープ以外は、親の変数の値に設定するにはラベルが必要です。ラベルなしは親と同じ名前の変数を新規作成します。親の変数の値は変わりません。
- スコープはAllScope、Global:、Script:、Local:、Private:、番号スコープがあります。
- AllScopeはどこからでも値を取得、設定できます。(非推奨)
- プロファイルの変数と関数のスコープはGlobalです。
- コマンドラインのスクリプト ブロックの中以外の変数と関数のスコープはGlobalです。
- ドット ソース形式で読み込むと、スクリプト ブロックの中以外は「Private:」を付けててもGlobalになります。
- Windows PowerShell ISEの「デバッグ」の「実行/続行」メニューで、スクリプト ブロックの中以外は「Private:」を付けててもGlobalになります。
- Global変数は子のスクリプト ブロックから読めます。
- スクリプト ブロックや関数の中からGlobal変数の値を変更するには、変数の名前の前に「Global:」を付けます。
- スクリプト ファイル内のスクリプト ブロックの中以外で、ラベルなしの変数のスコープはScriptです。同じファイルのスコープで、他のファイルから参照できません。
- スクリプト ファイル内のスクリプト ブロックや関数の中から、Script変数の値を変更するには、変数の名前の前に「Script:」を付けます。
- パイプライン処理のスクリプト ブロックのBegin、Process、Endの中の変数は参照できます。チュートリアルの「Get-LineCount」関数を参照してください。
- スコープを宣言しないとLocalになり、同じ階層から値を変更でき、子のスコープから読めます。スコープによってはGlobalやScriptになります。
- 変数の名前の前に「Local:」を付けると、親のスクリプト ブロックの変数と関係なくローカルになります。間違い易いので「Local:」は使用しないで、違う名前の変数にしましょう。
- 変数の名前の前に「Private:」を付けると、同じ階層でのみ値の取得と設定をします。親と子のスクリプト ブロック内から参照できません。
- Using:を付けるとリモートコマンド内で、呼び出し元のローカルセッションの変数になります。
- AllScopeはどこからでも値を取得、設定できます。(非推奨)
- 「Global:」でグローバルに変数を設定する
- 「Script:」でスクリプト ファイルの変数を設定する
- 番号スコープで値を取得する
- ドット ソース形式で読み込んだPrivateスコープ
- ドット ソース形式で読み込んだ設定変数のスコープ
- 配列、連想配列は子のスクリプト ブロックで設定できる
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 のままで変わりません。
実行してもルートの階層の変数の値は変わりません。
同じスクリプト ファイルで値を設定できるように、変数の名前の前にラベルの「Script:」を付けます。
$a = 0
&{
$a # 0
$Script:a = 1 # 1 スクリプトファイルの変数に代入します。
$Script:a # 1
}
$a # 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の変数は子のスクリプト ブロックで変更しても、親では変わりませんでした。