Powershellのスコープについて

Aug 24 2020

Powershellのスコープについて学習していて、いくつか質問があります。

  1. 「ローカルスコープ」について:私が読んだところによると、ローカルスコープは常に現在のスコープです。したがって、デフォルトでは、あるスコープで変数などのアイテム(スコープ修飾子なし)を作成する場合、それをスクリプトまたはグローバルにすると、スコープはそれに応じてスクリプト/グローバルになります。だから私の質問は:いつlocal修飾子を明示的に指定する必要があるのでしょうか?
  2. MSDNによると:

スクリプトまたは関数を実行するか、セッションを作成するか、PowerShellの新しいインスタンスを開始することにより、新しいスコープを作成できます。新しいスコープを作成すると、結果は親スコープ(元のスコープ)と子スコープ(作成したスコープ)になります。...
アイテムを明示的にプライベートにしない限り、親スコープのアイテムは子スコープで使用できます。ただし、子スコープで作成および変更したアイテムは、アイテムの作成時にスコープを明示的に指定しない限り、親スコープには影響しません。

しかし、私が以下を試してみると:

PS> $Name = "John" PS> Powershell.exe PS>echo $Name  // No Output

上記の引用から、「PowerShellの新しいインスタンスの開始」は子スコープであるように思われるため、親スコープ内のすべてのアイテムがそこに表示されるはずです。誰かが説明できますか?

回答

2 mklement0 Aug 24 2020 at 23:16

iRonの役立つ答えを補完するために:

  1. [...]ローカル修飾子を明示的に指定する必要があるのはいつですか?

$local:スコープ指定子がない場合はローカルスコープが暗示されるため、が必要になることはめったにありませ

しかし、それは、参照された変数が実際として存在している場合にのみ適用されるローカル変数のことを考えると、PowerShellののダイナミックスコープはからの変数になり先祖(親)だけでなくスコープ子孫(子)に見えるスコープ(参照この回答より多くの情報のために):

  • たとえば$foo = 'bar'グローバルスコープで宣言した場合$fooスクリプトで参照すると、最初にローカル $fooインスタンスが検索されます。存在しない場合は$foo祖先(親)スコープで定義されたものが使用されます。これは、この例ではグローバル $fooであり、'bar'返されます。

  • 対照的に、スクリプトで$local:fooローカル$foo変数を定義せずにを使用すると、$nullデフォルトで取得するか、Set-StrictMode -Version 2それ以上が有効な場合は、ステートメント終了エラーが発生します。


  1. MSDNによると:[...]セッションを作成するか、PowerShellの新しいインスタンスを開始すると[...]結果は親スコープ(元のスコープ)と子スコープ(作成したスコープ)になります。

ドキュメントがある間違ってこれを書いて(のように、この点でGitHubの問題が提起されています):

  • スコープ間の祖先(親子)関係は、特定のセッション(実行スペース)のコンテキストにのみ存在します。

    • つまり、動的スコープ(祖先スコープからの変数およびその他の定義の可視性)は、特定のセッション内のスコープにのみ適用されます。

    • 注目すべき例外は、モジュールの関数が呼び出し元のスコープの子スコープで実行されないことです。ただし、その呼び出し元のスコープがたまたまグローバルスコープである場合を除きます。モジュールには、グローバルスコープのみにリンクされた独自のスコープドメイン(技術的にはセッション状態と呼ばれます)があります。詳細については、このGitHubドキュメントの問題を参照してください。

  • そのため、何の子スコープ呼び出しスコープのは、次のシナリオで作成されていない、新たに立ち上げたコードは、呼び出し元のスコープ内の変数(および他の定義)の何も知っています

    • PowerShellリモーティング(例:with )を介して新しいセッションを開始するEnter-PSSessionまたはInvoke-Command -Computer

    • v7.0 +で、バックグラウンド[スレッド]ジョブを開始するStart-JobかStart-ThreadJob、スレッドを並行して実行しますForEach-Object -Parallel

    • PowerShell CLIを使用して新しいPowerShellインスタンス(プロセス)を開始します(pwshPowerShell [Core]の場合powershell.exe、Windows PowerShellの場合)。

    • これらのシナリオで呼び出しスコープから新しく起動されたコードに値を伝達するには、明示的なアクションが必要です

      • CLIを呼び出すか、同じマシン上にStart-Jobプロセスが作成されるを使用すると、呼び出しプロセスで定義された環境変数のみが子プロセスで自動的に使用可能になります。
      • それ以外の場合は、呼び出し元からの値を引数として渡すか、CLIを使用する場合を除いて$using:スコープを介して渡す必要があります。この回答を参照してください。
4 iRon Aug 24 2020 at 14:48

後者の質問から始めます:

スコープは、次のような関数および呼び出されたスクリプト(コマンドレット)と連携します。

Function Test {
    $Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test

戻り値:

Local: 6
Global: 5

そして:

Function Test {
    $Global:Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test

戻り値:

Local: 6
Global: 6

または、関数をスクリプトに配置した場合(例MyScript.ps1):

$Test = 5 .\MyScript.ps1 Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script

現在のスコープで実行されるスクリプトをドットソースしない限り、これは基本的に上記と同じ結果を返します。

$Test = 5 . .\MyScript.ps1 Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test

実行していること:変数の新しいリストで始まる
完全に新しいPowerShellセッション(を使用Powershell.exe)を作成しています。
ここでexit、新しいセッションからの場合は、初期変数が再び表示されることに注意してください。

PS C:\> $Name = "John" PS C:\> Powershell.exe Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. Try the new cross-platform PowerShell https://aka.ms/pscore6 PS C:\> Write-Host 'New session' $Name
New session
PS C:\> Exit
PS C:\> Write-Host 'Initial session' $Name
Initial session John

最初の質問に関しては、$Localスコープを明示的に参照する必要があるアプリケーションは多くないと思いますが、それを使用する可能性のある例を示します。

$Test = 5 Function Test { Write-Host ($Local:Test++)
}
Test

上記の例では、単項インクリメント演算子は0$Localスコープを明示的に使用する場合(実際にはにキャストされる空のローカル変数から開始0する5場合)、および変数の$Localコピーを継承するスコープを省略する場合に開始されます。$Test親スコープ。