Scopesについて

簡単な説明

PowerShell のスコープの概念と、要素のスコープを設定および変更する方法について説明します。

長い説明

PowerShell は、変数、エイリアス、関数、PowerShell ドライブ (PSDrive) へのアクセスを、読み取りおよび変更できる場所を制限することで保護します。 PowerShell では、スコープ ルールを使用して、変更すべきでない項目を誤って変更しないようにします。

スコープの基本的な規則を次に示します。

  • スコープは入れ子になる場合があります。 外部スコープは親スコープと呼ばれます。 入れ子になったスコープは、その親の子スコープです。

  • 項目は、明示的にプライベートにしない限り、作成されたスコープと任意の子スコープに表示されます。

  • 変数、エイリアス、関数、および PowerShell ドライブは、現在のスコープ外のスコープで宣言できます。

  • スコープ内で作成した項目は、別のスコープを明示的に指定しない限り、作成されたスコープ内でのみ変更できます。

スコープ内に項目を作成し、その項目の名前が別のスコープ内の項目と共有されている場合、元の項目は新しい項目の下に隠れる可能性がありますが、オーバーライドされたり変更されたりすることはありません。

PowerShell スコープ

PowerShell では、次のスコープがサポートされています。

  • グローバル: PowerShell の開始時、または新しいセッションまたは実行空間の作成時に有効なスコープ。 PowerShell の起動時に存在する変数と関数 (自動変数や基本設定変数など) は、グローバル スコープに作成されています。 PowerShell プロファイルの変数、エイリアス、関数もグローバル スコープで作成されます。 グローバル スコープは、セッション内のルートの親スコープです。

  • ローカル: 現在のスコープ。 ローカル スコープには、グローバル スコープまたはその他のスコープを指定できます。

  • スクリプト: スクリプト ファイルの実行中に作成されるスコープ。 スクリプト内のコマンドのみがスクリプト スコープで実行されます。 スクリプト内のコマンドにとって、スクリプト スコープはローカル スコープです。

プライベート はスコープではありません。 これは、アイテムが定義されているスコープ外のアイテムの可視性を変更する オプションです

親スコープと子スコープ

スクリプトまたは関数を呼び出すことで、新しい子スコープを作成できます。 呼び出し元のスコープは親スコープです。 呼び出されたスクリプトまたは関数は子スコープです。 呼び出す関数またはスクリプトは、他の関数を呼び出して、ルート スコープがグローバル スコープである子スコープの階層を作成する場合があります。

明示的にアイテムをプライベートにしない限り、親スコープ内の項目は子スコープで使用できます。 ただし、子スコープで作成および変更したアイテムは、アイテムの作成時にスコープを明示的に指定しない限り、親スコープに影響を与えません。

モジュールの関数は、呼び出し元のスコープの子スコープでは実行されません。 モジュールには、グローバルスコープにリンクされた独自のセッション状態があります。 すべてのモジュール コードは、独自のルート スコープを持つスコープのモジュール固有の階層で実行されます。

継承

子スコープは、親スコープから変数、エイリアス、および関数を継承しません。 アイテムがプライベートでない限り、子スコープは親スコープ内のアイテムを表示できます。 また、親スコープを明示的に指定することで項目を変更できますが、項目は子スコープの一部ではありません。

ただし、子スコープは一連の項目を使用して作成されます。 通常、 これには AllScope オプションを持つすべてのエイリアスが含まれます。 このオプションについては、この記事の後半で説明します。 これには、AllScope オプションを持つすべての変数と、いくつかの自動変数が含まれます。

特定のスコープ内の項目を検索するには、Get-Variable または Get-Aliasの Scope パラメーターを使用します。

たとえば、ローカル スコープ内のすべての変数を取得するには、次のように入力します。

Get-Variable -Scope local

グローバル スコープ内のすべての変数を取得するには、次のように入力します。

Get-Variable -Scope global

スコープ修飾子

変数、エイリアス、または関数名には、次の省略可能なスコープ修飾子のいずれかを含めることができます。

  • global: - グローバル スコープに名前が存在することを指定します。

  • local: - Local スコープに名前が存在することを指定します。 現在のスコープは常に Local スコープです。

  • private: - 名前がプライベートであり、現在のスコープにのみ表示されることを指定します。

  • script: - スクリプト スコープに名前が存在することを指定します。 スクリプト スコープは、最も近い先祖スクリプト ファイルのスコープです。最も近い先祖スクリプト ファイルがない場合はグローバル

  • using: - Start-JobInvoke-Command などのコマンドレットを使用してスクリプトを実行しているときに、別のスコープで定義された変数にアクセスするために使用されます。

  • workflow: - ワークフロー内に名前が存在することを指定します。 注: ワークフローは PowerShell v6 以降ではサポートされていません。

  • <variable-namespace> - PowerShell PSDrive プロバイダーによって作成される修飾子。 例えば次が挙げられます。

    Namespace 説明
    Alias: 現在のスコープで定義されているエイリアス
    Env: 現在のスコープで定義されている環境変数
    Function: 現在のスコープで定義されている関数
    Variable: 現在のスコープで定義されている変数

スクリプトの既定のスコープはスクリプト スコープです。 関数とエイリアスのデフォルトのスコープは、スクリプトで定義されている場合でも、ローカルスコープです。

スコープ修飾子の使用

新しい変数、エイリアス、または関数のスコープを指定するには、スコープ修飾子を使用します。

変数内のスコープ修飾子の構文は次のとおりです。

$[<scope-modifier>:]<name> = <value>

関数内のスコープ修飾子の構文は次のとおりです。

function [<scope-modifier>:]<name> {<function-body>}

次のコマンドは、スコープ修飾子を使用しないもので、現在のスコープまたは ローカル のスコープに変数を作成します。

$a = "one"

グローバル スコープに同じ変数を作成するには、スコープ global: 修飾子を使用します。

$global:a = "one"

スクリプト スコープに同じ変数を作成するには、script: スコープ修飾子を使用します。

$script:a = "one"

関数でスコープ修飾子を使用することもできます。 次の関数定義は、グローバル スコープに関数を作成します。

function global:Hello {
  Write-Host "Hello, World"
}

また、スコープ修飾子を使用して、別のスコープ内の変数を参照することもできます。 次のコマンドは、最初にローカル スコープで、次にグローバル スコープで、$test 変数を参照します。

$test
$global:test

Using: スコープ修飾子

Using は、リモート コマンドでローカル変数を識別する特殊なスコープ修飾子です。 修飾子がない場合、PowerShell はリモート コマンドの変数がリモート セッションで定義されることを想定しています。

Using スコープ修飾子は、PowerShell 3.0 で導入されています。

セッション外で実行されるスクリプトまたはコマンドの場合は、呼び出し元のセッション スコープから変数値を埋め込むために、Using スコープ修飾子が必要です。そのため、セッション外のコードがそれらにアクセスできます。 Using スコープ修飾子は、次のコンテキストでサポートされています。

  • Invoke-CommandHostNameSSHConnection、または Session パラメーター (リモート セッション) を使用して で開始されたリモート実行コマンド
  • バックグラウンド ジョブ、Start-Job で開始 (プロセス セッション外)
  • スレッド ジョブ(Start-ThreadJob または ForEach-Object -Parallel を使用して開始されます (個別のスレッド セッション)

コンテキストに応じて、埋め込み変数の値は、呼び出し元のスコープ内のデータの独立したコピーか、それに対する参照のいずれかになります。 リモート セッションとプロセス外セッションでは、これらは常に独立したコピーです。

詳細については、about_Remote_Variablesを参照してください。

スレッド セッションでは、これらはリファレンスで渡されます。 これは、別のスレッドで呼び出しスコープ変数を変更できることを意味します。 変数を安全に変更するには、スレッドの同期が必要です。

詳細については、以下を参照してください。

変数値のシリアル化

リモートで実行されたコマンドとバックグラウンド ジョブは、アウトプロセスで実行されます。 アウトプロセス セッションでは、XML ベースのシリアル化と逆シリアル化を使用して、プロセス境界を越えて変数の値を使用できるようにします。 シリアル化プロセスは、元のオブジェクトプロパティを含むがメソッドを含まない PSObject にオブジェクトを変換します。

型のセットが制限されている場合、逆シリアル化はオブジェクトを元の型にリハイドレートします。 リハイドレートされたオブジェクトは、元のオブジェクト インスタンスのコピーです。 それには型のプロパティとメソッドがあります。 System.Versionなどの単純型の場合、コピーは正確です。 複合型の場合、コピーは不完全です。 たとえば、リハイドレートされた証明書オブジェクトには秘密キーは含まれません。

他のすべての型のインスタンスは、PSObject インスタンスです。 PSTypeNames プロパティには、たとえば、Deserialized.System.Data.DataTable のように Deserialized が先頭に付いた元の型名が含まれます。

AllScope オプション

変数とエイリアスには、AllScopeの値を取ることができる Option プロパティがあります。 AllScope プロパティを持つ項目は、作成する子スコープの一部になりますが、親スコープにさかのぼって継承されることはありません。

AllScope プロパティを持つ項目は、子スコープに表示され、そのスコープの一部です。 任意のスコープ内の項目に対する変更は、変数が定義されているすべてのスコープに影響します。

スコープの管理

いくつかのコマンドレットには、特定のスコープ内のアイテムを取得または設定 (作成および変更) できる Scope パラメーターがあります。 次のコマンドを使用して、Scope パラメーターを持つセッション内のすべてのコマンドレットを検索します。

Get-Help * -Parameter scope

特定のスコープに表示される変数を検索するには、ScopeGet-Variable パラメーターを使用します。 表示される変数には、グローバル変数、親スコープの変数、および現在のスコープ内の変数が含まれます。

たとえば、次のコマンドは、ローカル スコープに表示される変数を取得します。

Get-Variable -Scope local

特定のスコープに変数を作成するには、スコープ修飾子または という Set-Variable パラメーターを使用します。 次のコマンドは、グローバル スコープに変数を作成します。

New-Variable -Scope global -Name a -Value "One"

また、New-AliasSet-Alias、または Get-Alias コマンドレットの Scope パラメーターを使用して、スコープを指定することもできます。 次のコマンドは、グローバル スコープにエイリアスを作成します。

New-Alias -Scope global -Name np -Value Notepad.exe

特定のスコープ内の関数を取得するには、スコープ内にいるときに Get-Item コマンドレットを使用します。 Get-Item コマンドレットには Scope パラメーターがありません。

Scope パラメーターを使用するコマンドレットについては、スコープを数値で参照することもできます。 この数値は、あるスコープから別のスコープへの相対位置を表します。 スコープ 0 は、現在のスコープまたはローカル スコープを表します。 スコープ 1 は、直接の親スコープを示します。 スコープ 2 は親スコープの親を示す、という形です。 番号付きスコープは、多くの再帰スコープを作成した場合に便利です。

スコープでのドットソース表記の使用

スクリプトと関数は、スコープのすべてのルールに従います。 これらは特定のスコープに作成します。コマンドレット パラメーターまたはスコープ修飾子を使用してそのスコープを変更しない限り、そのスコープにのみ影響します。

ただし、ドットソース表記を使用して、スクリプトまたは関数を現在のスコープに追加できます。 その後、スクリプトが現在のスコープで実行されると、スクリプトが作成するすべての関数、エイリアス、および変数が現在のスコープで使用できます。

現在のスコープに関数を追加するには、関数呼び出しの関数のパスと名前の前にドット (.) とスペースを入力します。

たとえば、スクリプト スコープの C:\Scripts ディレクトリ (スクリプトのデフォルト) から Sample.ps1 スクリプトを実行するには、次のコマンドを使用します。

c:\scripts\sample.ps1

Sample.ps1 スクリプトをローカル スコープで実行するには、次のコマンドを使用します。

. c:\scripts.sample.ps1

呼び出し演算子 (&) を使用して関数またはスクリプトを実行する場合、その関数またはスクリプトは現在のスコープに追加されません。 次の例では、call 演算子を使用しています。

& c:\scripts.sample.ps1

通話オペレーターの詳細については、 about_operatorsをご覧ください。

Sample.ps1 スクリプトが作成するエイリアス、関数、または変数は、現在のスコープでは使用できません。

スコープなしの制限

いくつかの PowerShell の概念は、スコープと似ているか、スコープと対話します。 これらの概念は、スコープまたはスコープの動作と混同される可能性があります。

セッション、モジュール、およびネストされたプロンプトは自己完結型の環境ですが、セッション内のグローバルスコープの子スコープではありません。

セッション

セッションは、PowerShell を実行する環境です。 リモート コンピューターでセッションを作成すると、PowerShell によってリモート コンピューターへの永続的な接続が確立されます。 永続的な接続を使用すると、複数の関連コマンドにセッションを使用できます。

セッションは包含環境であるため、独自のスコープを持ちますが、セッションは作成されたセッションの子スコープではありません。 セッションは、独自のグローバル スコープで開始されます。 このスコープは、セッションのグローバル スコープとは無関係です。 セッションで子スコープを作成できます。 たとえば、スクリプトを実行して、セッションに子スコープを作成できます。

モジュール

PowerShell モジュールを使用して、PowerShell ツールを共有および配信できます。 モジュールは、コマンドレット、スクリプト、関数、変数、エイリアス、およびその他の便利な項目を含めることができるユニットです。 明示的に定義されていない限り、モジュール内の項目はモジュールの外部からアクセスできません。 そのため、セッションにモジュールを追加し、他の項目がセッション内のコマンドレット、スクリプト、関数、およびその他の項目をオーバーライドする可能性があることを気にせずにパブリック項目を使用できます。

デフォルトでは、モジュールは現在のスコープではなく、現在のセッション状態の最上位にロードされます。 現在のセッション状態は、モジュール セッション状態またはグローバル セッション状態です。 セッションにモジュールを追加しても、スコープは変更されません。 グローバル スコープ内にある場合、モジュールはグローバル セッション状態にロードされます。 すべてのエクスポートはグローバルテーブルに配置されます。 module1 から module2 をロードすると、module2 はグローバル セッション ステートではなく module1 のセッション ステートにロードされます。 module2 からのエクスポートは、module1 セッション状態の最上位に配置されます。 Import-Module -Scope localを使用すると、エクスポートは最上位ではなく現在のスコープ オブジェクトに配置されます。 モジュール内にいて、Import-Module -Scope global (または Import-Module -Global) を使用して別のモジュールをロードする場合、そのモジュールとそのエクスポートは、モジュールのローカル セッション状態ではなく、グローバル セッション状態にロードされます。 この機能は、モジュールを操作するモジュールを書くために設計されました。 WindowsCompatibility モジュールは、プロキシ モジュールをグローバル セッション状態にインポートするためにこれを行います。

セッション状態内では、モジュールには独自のスコープがあります。 次のモジュール C:\temp\mod1.psm1考えてみましょう。

$a = "Hello"

function foo {
    "`$a = $a"
    "`$global:a = $global:a"
}

次に、グローバル変数 $aを作成し、値を指定し、foo 関数を呼び出します。

$a = "Goodbye"
foo

モジュールはモジュール スコープで変数 $a を宣言し、foo 関数 両方のスコープで変数の値を出力します。

$a = Hello
$global:a = Goodbye

ネストされたプロンプト

ネストされたプロンプトには、独自のスコープはありません。 入れ子になったプロンプトを入力すると、入れ子になったプロンプトは環境のサブセットになります。 ただし、ローカル スコープ内に残ります。

スクリプトには独自のスコープがあります。 スクリプトをデバッグしていて、スクリプトのブレークポイントに到達した場合は、スクリプト スコープを入力します。

プライベートオプション

エイリアスと変数には、Private の値を取ることができる Option プロパティがあります。 [プライベート] オプションを持つアイテムは、作成されたスコープで表示および変更できますが、そのスコープ外で表示または変更することはできません。

たとえば、グローバル スコープにプライベート オプションを持つ変数を作成し、スクリプトを実行した場合、スクリプト内の Get-Variable コマンドにはプライベート変数が表示されません。 このインスタンスでグローバル スコープ修飾子を使用すると、プライベート変数は表示されません。

New-VariableSet-VariableNew-Alias、および Set-Alias コマンドレットの Option パラメーターを使用して、Option プロパティの値を Private に設定できます。

視認性

変数またはエイリアスの Visibility プロパティによって、作成されたコンテナーの外部にある項目を表示できるかどうかが決まります。 コンテナーには、モジュール、スクリプト、またはスナップインを指定できます。 可視性は、Option プロパティの Private 値がスコープ用に設計されているのと同じように、コンテナー用に設計されています。

Visibility プロパティは、PublicPrivate の値を取ります。 プライベート可視性を持つ項目は、作成されたコンテナーでのみ表示および変更できます。 コンテナが追加またはインポートされた場合、プライベート表示を持つアイテムは表示または変更できません。

可視性はコンテナー用に設計されているため、スコープ内では動作が異なります。

  • グローバル スコープでプライベート表示を持つアイテムを作成した場合、どのスコープでもアイテムを表示または変更することはできません。
  • プライベート可視性を持つ変数の値を表示または変更しようとすると、PowerShell はエラー メッセージを返します。

New-Variable コマンドレットと Set-Variable コマンドレットを使用して、プライベート可視性を持つ変数を作成できます。

例示

例 1: スクリプト内でのみ変数値を変更する

次のコマンドは、スクリプト内の $ConfirmPreference 変数の値を変更します。 この変更は、グローバル スコープには影響しません。

まず、$ConfirmPreference 変数の値をローカル スコープに表示するには、次のコマンドを使用します。

PS>  $ConfirmPreference
High

次のコマンドを含む Scope.ps1 スクリプトを作成します。

$ConfirmPreference = "Low"
"The value of `$ConfirmPreference is $ConfirmPreference."

スクリプトを実行します。 スクリプトは、$ConfirmPreference 変数の値を変更し、その値をスクリプト スコープに報告します。 出力は次の出力のようになります。

The value of $ConfirmPreference is Low.

次に、現在のスコープ内の $ConfirmPreference 変数の現在の値をテストします。

PS>  $ConfirmPreference
High

この例では、スクリプト スコープ内の変数の値を変更しても、親スコープ内の変数の値に影響しないことを示しています。

例 2: 異なるスコープで変数値を表示する

スコープ修飾子を使用して、ローカル スコープと親スコープ内の変数の値を表示できます。

まず、グローバル スコープで $test 変数を定義します。

$test = "Global"

次に、$test 変数を定義する Sample.ps1 スクリプトを作成します。 このスクリプトでは、スコープ修飾子を使用して、$test 変数のグローバル バージョンまたはローカル バージョンを参照します。

Sample.ps1 では:

$test = "Local"
"The local value of `$test is $test."
"The global value of `$test is $global:test."

Sample.ps1を実行すると、出力は次の出力のようになります。

The local value of $test is Local.
The global value of $test is Global.

スクリプトが完了すると、$test のグローバル値のみがセッションで定義されます。

PS>  $test
Global

例 3: 親スコープ内の変数の値を変更する

Private オプションまたは別の方法を使用して項目を保護しない限り、親スコープ内の変数の値を表示および変更できます。

まず、グローバル スコープで $test 変数を定義します。

$test = "Global"

次に、$test 変数を定義する Sample.ps1 スクリプトを作成します。 このスクリプトでは、スコープ修飾子を使用して、$test 変数のグローバル バージョンまたはローカル バージョンを参照します。

Sample.ps1 では:

$global:test = "Local"
"The global value of `$test is $global:test."

スクリプトが完了すると、$test のグローバル値が変更されます。

PS>  $test
Local

例 4: プライベート変数の作成

プライベート変数は、値が PrivateOption プロパティを持つ変数です。 プライベート 変数は子スコープに継承されますが、表示または変更できるのは、その変数が作成されたスコープ内のみです。

次のコマンドは、ローカル スコープに $ptest というプライベート変数を作成します。

New-Variable -Name ptest -Value 1 -Option private

ローカル スコープで $ptest の値を表示および変更できます。

PS>  $ptest
1

PS>  $ptest = 2
PS>  $ptest
2

次に、次のコマンドを含む Sample.ps1 スクリプトを作成します。 このコマンドは、 $ptest の値を表示および変更しようとします。

Sample.ps1 では:

"The value of `$Ptest is $Ptest."
"The value of `$Ptest is $global:Ptest."

$ptest変数はスクリプトスコープに表示されず、出力は空です。

"The value of $Ptest is ."
"The value of $Ptest is ."

例 5: リモートコマンドでのローカル変数の使用

ローカル セッションで作成されたリモート コマンドの変数の場合は、Using スコープ修飾子を使用します。 PowerShell では、リモート コマンドの変数がリモート セッションで作成されたものと想定しています。

構文は次のとおりです。

$Using:<VariableName>

たとえば、次のコマンドでは、ローカル セッションで $Cred 変数を作成し、リモート コマンドで $Cred 変数を使用します。

$Cred = Get-Credential
Invoke-Command $s {Remove-Item .\Test*.ps1 -Credential $Using:Cred}

Using スコープは PowerShell 3.0 で導入されました。 PowerShell 2.0 では、変数がローカル セッションで作成されたことを示すには、次のコマンド形式を使用します。

$Cred = Get-Credential
Invoke-Command $s {
  param($c)
  Remove-Item .\Test*.ps1 -Credential $c
} -ArgumentList $Cred

こちらも参照ください