web-dev-qa-db-ja.com

PowerShellの変数スコープ

PowerShellの悲しい点は、関数とスクリプトブロックが動的にスコープされることです。

しかし、私が驚いた別のことは、変数が内部スコープ内でコピーオンライトとして動作することです。

$array=@("g")
function foo()
{
    $array += "h"
    Write-Host $array
}

& {
    $array +="s"
    Write-Host $array
}
foo

Write-Host $array

出力は次のとおりです。

g s
g h
g

これにより、動的スコープの痛みが少し軽減されます。しかし、コピーオンライトを回避するにはどうすればよいですか?

61
mathk

scope修飾子または*-Variableコマンドレットを使用できます。

スコープ修飾子は次のとおりです。

  • globalは、最も外側のスコープ(たとえば、対話型シェル)でアクセス/変更するために使用されます
  • scriptは、実行中のスクリプト(.ps1ファイル)のスコープでアクセス/変更に使用されます。スクリプトを実行していない場合、globalとして動作します。

-Scopeコマンドレットの*-Variableパラメーターについては、ヘルプを参照してください。)

例えば。 2番目の例では、グローバル$arrayを直接変更します。

& {
  $global:array +="s"
  Write-Host $array
}

詳細については、ヘルプトピックを参照してください。 about_scopes

70
Richard

PowerShellスコープの記事( about_Scopes )は素晴らしいですが、冗長すぎるため、これは私の article からの引用です:

一般に、PowerShellスコープは.NETスコープに似ています。彼らです:

  • Globalはパブリックです
  • スクリプトは内部
  • Privateはプライベートです
  • Localは現在のスタックレベルです
  • 番号付きスコープは0..Nからであり、各ステップはスタックレベルまでです(0はローカルです)

次に、スコープの使用法と効果を説明する簡単な例を示します。

$test = 'Global Scope'
Function Foo {
    $test = 'Function Scope'
    Write-Host $Global:test                                  # Global Scope
    Write-Host $Local:test                                   # Function Scope
    Write-Host $test                                         # Function Scope
    Write-Host (Get-Variable -Name test -ValueOnly -Scope 0) # Function Scope
    Write-Host (Get-Variable -Name test -ValueOnly -Scope 1) # Global Scope
}
Foo

ご覧のとおり、名前付きスコープでのみ$ Global:testのような構文を使用できます。$ 0:testは常に$ nullです。

85
Anton Purin

単なる変数ではありません。これが「アイテム」と言うとき、変数、関数、エイリアス、およびpsdriveを意味します。これらはすべてスコープを持っています。

詳細説明
 Windows PowerShellは、読み取りおよび
の変更が可能な場所を制限することにより、変数、エイリアス、関数、および
 Windows PowerShellドライブ(PSDrive)へのアクセスを保護します。範囲にいくつかの簡単なルールを適用することにより、Windows PowerShell 
は、変更してはならない
 
 
のアイテムを誤って変更しないようにするのに役立ちます。スコープの基本ルールは次のとおりです。
 
-スコープに含めるアイテムは、明示的に作成しない限り、それが作成されたスコープおよび子スコープで表示されます。 it 
 private。変数、エイリアス、関数、またはWindows 
 PowerShellドライブを1つ以上のスコープに配置できます。
 
-スコープ内で作成したアイテムは、
異なるスコープを明示的に指定しない限り、作成されたスコープ。

コピーオンライトの問題は、Powershellが配列を処理する方法が原因です。その配列に追加すると、実際には元の配列が破壊され、新しい配列が作成されます。そのスコープで作成されたため、関数またはスクリプトブロックが終了し、スコープが破棄されると破棄されます。

更新時に変数を明示的にスコープするか、[ref]オブジェクトを使用して更新を行うか、スクリプトを記述して、オブジェクトのプロパティまたはオブジェクトまたはハッシュテーブルのハッシュテーブルキーを更新します。親スコープ。これは、ローカルスコープ内に新しいオブジェクトを作成するのではなく、親スコープ内のオブジェクトを変更します。

16
mjolinor

他の投稿は多くの有用な情報を提供しますが、それらはRTFMからあなたを救うだけのようです。
言及されていない答えは、私が最も役立つと思う答えです!

([ref]$var).value = 'x'

これにより、$ varの値が変更されます。どのスコープに属しているかに関係なく、そのスコープを知る必要はありません。それが実際に既に存在するということだけです。 OPの例を使用するには:

$array=@("g")
function foo()
{
    ([ref]$array).Value += "h"
    Write-Host $array
}
& {
    ([ref]$array).Value +="s"
    Write-Host $array
}
foo
Write-Host $array

生産物:

g s
g s h
g s h

説明:
([ref] $ var)は、変数へのポインタを取得します。これは読み取り操作であるため、実際にその名前を作成した最新のスコープに解決されます。また、[ref]は何も作成できないため、変数が存在しない場合のエラーについても説明します。既に存在するものへの参照のみを返すことができます。

。valueその後、変数の定義を保持するプロパティに移動します。これを設定できます。

うまくいくように見えることがあるので、このようなことをしたくなるかもしれません。

([ref]$var) = "New Value"

いけない!!!!
PowerShellはコマンドラインなどの非常に狭い状況でのみ行うことを行っているため、動作しているように見えるインスタンスは幻想です。あなたはそれに頼ることはできません。実際、OPの例では機能しません。

1
bielawski