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 # => 5
1つ上の親スコープを指定しているので、変更が反映されている。
ところが、よく観察してみると少なくとも以下のような例外がある。
少なくともこの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と表示される。
うーん、何かしら例外規則的なものがあるのだろうか?