PowerShellでは基本的に、親スコープの変数を参照することはできても、それを書き換えることはできない。この規則はスクリプトプロックにも適用されている。
$x = 1
2..5 | & {process{$x = $_}}
$x
# => 1このコードを実行すると「1」が表示される。スクリプト化して実行しても同じだ。
実はドットソース演算子を使ってスクリプトブロックを実行することで親スコープの変数を変更できる――
$x = 1
2..5 | . {process{$x = $_}}
$x
# => 5――ように見えるのだけど、これはどちらかと言えば親スコープに変更を及ぼしているというよりも、子スコープを作らずに元のスコープで処理を実行している、という理解の方が正しい。
この辺りは「Get-Help about_Scopes」内に記述がある。
スクリプトおよび関数は、スコープのすべての規則に従います。スクリプトおよび関数は特定のスコープ内に作成され、コマンドレット パラメーターまたはスコープ修飾子を使用してスコープを変更しない限り、このスコープのみに作用します。
ただし、ドット ソース表記を使用することで、スクリプトまたは関数を現在のスコープに追加できます。この場合、スクリプトが現在のスコープ内で実行されると、スクリプトによって作成されるすべての関数、エイリアス、および変数が現在のスコープで利用可能になります。
http://technet.microsoft.com/ja-jp/library/dd315289.aspx
もし親スコープの変数を書き換えたいのなら、スコープ修飾子やSet-Variableのオプション-Scopeを使用して、どのスコープの変数なのか明確にする必要がある。この方法は、同じ名前の子スコープの変数によって隠れてしまった親スコープの変数を参照する際にも有効だ。ただしプライベート変数を除く。
$x = 1
2..5 | & {process{Set-Variable x -Scope 1 -Value $_}}
$x
# => 51つ上の親スコープを指定しているので、変更が反映されている。
ところが、よく観察してみると少なくとも以下のような例外がある。
少なくともこの2つのシチュエーションでは、スクリプトブロック内でスコープを明確にせずに1つ上の親スコープの変数を変更できるように見える。
$x = 1
2..5 | %{$x = $_}
$x
# => 5このコードは、PowerShellのプロンプト上で手動で実行しても、スクリプト化して実行しても、結果として「5」が表示される。この処理を関数化して実行した場合も同じ。
イベントハンドラの件は、Add_Click()などのメソッドの引数に直書きしたスクリプトブロックの中で1つ上の親スコープの変数を書き換えると、普通に変更が反映される。
# You must execute this script in STA mode
# $ powershell -Sta -File <this script>
Set-StrictMode -version Latest
Add-Type -assemblyName PresentationFramework
$xaml = @'
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="$APP_TITLE"
Width="20" Height="100"
>
<StackPanel>
<Button Name="button" Content="OK" />
</StackPanel>
</Window>
'@
$window = [System.Windows.Markup.XamlReader]::Parse($xaml)
$clicked = $False
$button = $window.FindName('button')
$button.Add_Click({
$clicked = $True
$window.Close()
})
$window.ShowDialog() | Out-Null
$clicked変数$clickedの書き換えが反映されるので、OKボタンを押下した場合のみTrueと表示される。それ以外の場合はFalseと表示される。
うーん、何かしら例外規則的なものがあるのだろうか?