about_Scopes

簡短描述

說明 PowerShell 中的範圍概念,並示範如何設定和變更元素的範圍。

詳細描述

PowerShell 會限制可以讀取和變更變數、別名、函式和 PowerShell 磁碟驅動器 (PSDrives) 的存取。 PowerShell 會使用範圍規則來確保您不會對其他範圍中的專案進行意外變更。

範圍規則

當您啟動 PowerShell 時,主機 (pwsh.exe) 會建立 PowerShell Runspace。 主機進程可以有多個 Runspace。 每個 Runspace 都有自己的工作階段狀態和範圍容器。 會話狀態和範圍無法在 Runspace 實例之間存取。

以下是範圍的基本規則:

  • 範圍可能會巢狀。 外部範圍稱為父範圍。 任何巢狀範圍都是該父系的子範圍。
  • 除非明確將其設為私用,否則項目會顯示在已建立的範圍和任何子範圍中。
  • 您可以針對目前範圍以外的範圍宣告變數、別名、函式和 PowerShell 磁碟驅動器。
  • 除非您明確指定不同的範圍,否則您在範圍內建立的專案只能在建立範圍中變更。
  • 在 Runspace 中執行的程式代碼會參考專案時,PowerShell 會搜尋範圍階層,從目前的範圍開始,然後逐一查看每個父範圍。 如果找不到專案,則會在目前範圍中建立新的專案。 如果找到相符專案,則會從找到的範圍擷取專案的值。 如果您變更值,則專案會複製到目前範圍,讓變更只會影響目前的範圍。
  • 如果您明確建立與不同範圍中的專案共用其名稱的專案,新專案可能會隱藏原始專案,但不會覆寫或變更。

父範圍和子範圍

您可以呼叫文稿或函式來建立新的子範圍。 呼叫範圍是父範圍。 呼叫的腳本或函式是子範圍。 您呼叫的函式或腳本可能會呼叫其他函式,建立根範圍是全域範圍的子範圍階層。

注意

來自模組的函式不會在呼叫範圍的子範圍中執行。 模組有自己的會話狀態,連結到模組匯入的範圍。 所有模組程式代碼都會在具有本身根範圍的模組特定範圍階層中執行。 如需詳細資訊,請參閱 本文的<> >一節。

建立子範圍時,它會包含具有 AllScope 選項的所有別名和變數,以及一些自動變數。 本文稍後會討論此選項。

除非您明確將項目設為私用,否則父範圍中的專案可供子範圍使用。 除非您在建立項目時明確指定範圍,否則您在子範圍中建立或變更的專案不會影響父範圍。

若要尋找特定範圍中的專案,請使用 或Get-AliasGet-Variable Scope 參數。

例如,若要取得本機範圍中的所有變數,請輸入:

Get-Variable -Scope local

若要取得全域範圍中的所有變數,請輸入:

Get-Variable -Scope global

當對變數、別名或函式進行參考時,PowerShell 會搜尋目前的範圍。 如果找不到專案,則會搜尋父範圍。 此搜尋會一直重複到全域範圍。 如果變數在父範圍中是私用的,則透過範圍鏈結繼續搜尋。 範例 4 顯示範圍搜尋中私用變數的效果。

PowerShell 範圍名稱

PowerShell 會定義某些範圍的名稱,以方便存取該範圍。 PowerShell 會定義下列具名範圍:

  • 全域:P owerShell 啟動時或建立新會話或 Runspace 時,生效的範圍。 PowerShell 啟動時出現的變數和函式,例如自動變數和喜好設定變數,會在全域範圍中建立。 PowerShell 配置檔中的變數、別名和函式也會在全域範圍中建立。 全域範圍是 Runspace 中的根父範圍。
  • 本機:目前的範圍。 本機範圍可以是全域範圍或任何其他範圍。
  • 腳本:腳本檔案執行時所建立的範圍。 文稿中的命令會在文稿範圍中執行。 針對腳本中的命令,腳本範圍是本機範圍。

對於支援範圍的 Cmdlet,範圍可由描述某個範圍相對位置的數字來參考。 範圍 0 代表目前的 (local) 範圍,範圍 1 是目前範圍的父系,範圍 2 是目前範圍的祖父母。 此模式會繼續執行,直到您到達根範圍為止。

範圍修飾詞

變數、別名或函式名稱可以包含下列任一選擇性範圍修飾詞:

  • global: - 指定名稱存在於 全域 範圍中。

  • local:- 指定名稱存在於本機範圍中。 目前的範圍一律是本機範圍。

  • private: - 指定名稱為 Private ,且只顯示於目前範圍中。

    注意

    private: 不是範圍。 這是一個選項,可變更專案在定義範圍之外之專案的存取範圍。

  • script: - 指定名稱存在於 文稿 範圍中。 腳本 範圍是最近的上階腳本檔案範圍,如果沒有最接近的上階腳本檔案,則 為 Global

  • using:- 用來透過 和 Invoke-CommandStart-Job Cmdlet 執行腳本時,存取在另一個範圍中定義的變數。

  • 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"
Get-Variable a | Format-List *

請注意 VisibilityOptions 屬性值。

Name        : a
Description :
Value       : one
Visibility  : Public
Module      :
ModuleName  :
Options     : None
Attributes  : {}

比較該變數與私用變數:

$private:pVar = 'Private variable'
Get-Variable pVar | Format-List *

使用範圍修飾詞會將 privateOptions 屬性設定為 Private

Name        : pVar
Description :
Value       : Private variable
Visibility  : Public
Module      :
ModuleName  :
Options     : Private
Attributes  : {}

若要在 腳本 範圍中建立相同的變數,請使用 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-Command為使用 ComputerName、HostNameSSH 連線 ion會話參數 (遠端會話)
  • 背景工作,從 Start-Job (跨進程工作)
  • 線程作業,透過 Start-ThreadJobForEach-Object -Parallel 啟動 (個別線程會話)

根據內容,內嵌變數值可以是呼叫端範圍中數據的獨立複本,或是參考數據。 在遠端和跨進程會話中,它們一律是獨立的複本。

如需詳細資訊,請參閱 about_Remote_Variables

在線程會話中,它們會以傳址方式傳遞。 這表示可以修改不同線程中的子範圍變數。 若要安全地修改變量,需要線程同步處理。

如需詳細資訊,請參閱:

變數值的串行化

遠端執行的命令和背景作業會用盡進程。 跨進程會話會使用以 XML 為基礎的串行化和還原串行化,讓變數的值可跨進程界限使用。 串行化程式會將物件 轉換成包含原始物件屬性但不包含其方法的 PSObject

針對一組有限的類型,還原串行化會將物件重新凍結回原始類型。 解除凍結的對像是原始物件實例的複本。 它具有型別屬性和方法。 對於簡單類型,例如 System.Version,複本是確切的。 對於複雜類型,復本不完美。 例如,解除凍結的憑證物件不包含私鑰。

所有其他類型的實例都是 PSObject 實例。 PSTypeNames 屬性包含前面加上還原串行化的原始類型名稱,例如 Deserialized.System.Data.DataTable

AllScope 選項

變數和別名具有 Option 屬性,可接受 AllScope 的值。 具有 AllScope 屬性的項目會成為您所建立之任何子範圍一部分,不過它們不會由父範圍追溯繼承。

具有 AllScope 屬性的項目會顯示在子範圍中,而且它是該範圍的一部分。 變更任何範圍中的項目會影響定義變數的所有範圍。

管理範圍

數個 Cmdlet 具有 Scope 參數,可讓您取得或設定特定範圍中的專案(建立和變更)。 使用下列命令來尋找工作階段中具有 Scope 參數的所有 Cmdlet:

Get-Help * -Parameter scope

若要尋找在特定範圍中可見的變數,請使用 ScopeGet-Variable參數。 可見變數包括全域變數、父範圍中的變數,以及目前範圍中的變數。

例如,下列命令會取得本機範圍中可見的變數:

Get-Variable -Scope local

若要在特定範圍中建立變數,請使用的範圍修飾詞或 的 Scope 參數 Set-Variable。 下列命令會在全域範圍中建立變數:

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

您也可以使用、 Set-AliasGet-Alias Cmdlet 的 New-AliasScope 參數來指定範圍。 下列命令會在全域範圍中建立別名:

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

若要取得特定範圍中的函式,請在範圍中時使用 Get-Item Cmdlet。 Cmdlet Get-Item 沒有 Scope 參數。

注意

對於使用 Scope 參數的 Cmdlet,您也可以依編號參照範圍。 此數位描述一個範圍到另一個範圍的相對位置。 範圍 0 代表目前或本機範圍。 範圍 1 表示直接父範圍。 範圍 2 表示父範圍的父代等等。 如果您已建立許多遞歸範圍,則編號範圍很有用。

搭配範圍使用點來源表示法

腳本和函式會遵循範圍規則。 您會在特定範圍中建立它們,而且它們只會影響該範圍,除非您使用 Cmdlet 參數或範圍修飾詞來變更該範圍。

但是,您可以使用點來源表示法,將腳本或函式的內容新增至目前的範圍。 當您使用點來源表示法執行腳本或函式時,它會在目前的範圍中執行。 腳本或函式中的任何函式、別名和變數都會新增至目前的範圍。

例如,若要 Sample.ps1C:\Scripts 腳本範圍中的目錄執行腳本(腳本的預設值),只要在命令行上輸入腳本檔案的完整路徑即可。

c:\scripts\sample.ps1

腳本檔案必須具有 .ps1 可執行檔的擴展名。 路徑中具有空格的檔案必須以引號括住。 如果您嘗試執行引號路徑,PowerShell 會顯示引號字串的內容,而不是執行腳本。 呼叫運算子 (&) 可讓您執行包含檔名的字串內容。

使用呼叫運算符來執行函式,或腳本會在腳本範圍中執行。 使用呼叫運算子與依名稱執行腳本並無不同。

& c:\scripts\sample.ps1

您可以在 about_Operators深入瞭解呼叫運算子。

若要在本機範圍中執行 Sample.ps1 文稿,請在腳稿的路徑之前輸入點和空格 (.

. c:\scripts\sample.ps1

現在,腳本中定義的任何函式、別名或變數都會新增至目前的範圍。

限制沒有範圍

PowerShell 有一些類似範圍的選項和功能,可能會與範圍互動。 這些功能可能會與範圍或範圍的行為混淆。

會話、模組和巢狀提示是獨立的環境,而不是會話中全域範圍的子範圍。

工作階段

會話是PowerShell執行所在的環境。 當您在遠端電腦上建立會話時,PowerShell 會建立與遠端電腦的持續性連線。 持續性連線可讓您針對多個相關命令使用會話。

因為會話是自主環境,所以它有它自己的範圍,但會話不是建立會話的子範圍。 會話會從它自己的全域範圍開始。 此範圍與會話的全域範圍無關。 您可以在工作階段中建立子範圍。 例如,您可以執行腳本,在會話中建立子範圍。

模組

您可以使用 PowerShell 模組來共享和傳遞 PowerShell 工具。 模組是一個單位,可以包含 Cmdlet、腳本、函式、變數、別名和其他實用專案。 除非明確匯出 (使用 Export-ModuleMember 或 模組指令清單),否則模組中的項目無法在模組外部存取。 因此,您可以將模組新增至您的會話,並使用公用專案,而不必擔心其他專案可能會覆寫會話中的 Cmdlet、腳本、函式和其他專案。

根據預設,模組會載入 Runspace 的根層級(全域)範圍。 匯入模組並不會變更範圍。 在工作階段中,模組有自己的範圍。 請考慮下列課程模組 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

模組會建立連結至匯入範圍之範圍的平行範圍容器。 模組導出的專案可從匯入的範圍層級開始取得。 未從模組導出的專案只能在模組的範圍容器內使用。 模組中的函式可以存取其匯入範圍中的專案,以及模組範圍容器中的專案。

如果您從Module1載入Module2,則Module2會載入Module1的範圍容器。 從Module2的任何導出都放在Module1的目前模組範圍中。 如果您使用 Import-Module -Scope local,則匯出會放入目前的範圍物件中,而不是放在最上層。 如果您在 模組 中,並使用 Import-Module -Scope global (或 Import-Module -Global) 載入另一個模組,該模組及其匯出會載入全域範圍,而不是模組的本機範圍。 WindowsCompatibility 功能會執行這項作業,將 Proxy 模組匯入全域會話狀態。

巢狀提示

巢狀提示沒有自己的範圍。 當您輸入巢狀提示時,巢狀提示是環境的子集。 但是,您仍會留在本機範圍內。

腳本確實有自己的範圍。 如果您要偵錯文本,並在腳本中觸達斷點,請輸入腳本範圍。

私人選項

別名和變數具有 Option 屬性,可接受 的值 Private。 具有 Private 選項的專案可以在建立所在的範圍中檢視和變更,但無法在該範圍之外檢視或變更。

例如,如果您在全域範圍中建立具有私用選項的變數,然後執行腳本, Get-Variable 則腳本中的命令不會顯示私用變數。 在此實例中使用全域範圍修飾詞不會顯示私用變數。

您可以使用 Set-VariableNew-Alias、 和 Set-Alias Cmdlet 的 New-VariableOption 參數,將 Option 屬性的值設定為 Private。

能見度

變數或別名的 Visibility 屬性會決定您是否可以看到容器外部的專案,在其中建立該專案。 容器可以是模組、腳本或嵌入式管理單元。 Visibility 是針對容器所設計,其方式與 Private Option屬性的值針對範圍所設計的方式相同。

Visibility 屬性會採用 PublicPrivate 值。 只有建立私用可見性的專案才能在容器中檢視和變更。 如果已新增或匯入容器,則無法檢視或變更具有私用可見度的專案。

因為可見度是針對容器所設計,所以在範圍中的運作方式不同。

  • 如果您在全域範圍中建立具有私用可見度的專案,則無法檢視或變更任何範圍中的專案。
  • 如果您嘗試檢視或變更具有私用可見性的變數值,PowerShell 會傳回錯誤訊息。

您可以使用 New-VariableSet-Variable Cmdlet 來建立具有私用可見度的變數。

範例

範例 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"

接下來,建立 Sample.ps1 定義變數的 $test 腳本。 在腳本中,使用範圍修飾詞來參考變數的 $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:建立私用變數

您可以使用範圍修飾詞或建立選項屬性設定為 Private的變數,將變數設為 私private:用。 私用變數只能在建立私用變數的範圍中檢視或變更。

在此範例中,腳本會建立五個 ScopeExample.ps1 函式。 第一個函式會呼叫下一個函式,這會建立子範圍。 其中一個函式具有私用變數,只能在建立它的範圍中看到。

PS> Get-Content ScopeExample.ps1
# Start of ScopeExample.ps1
function funcA {
    "Setting `$funcAVar1 to 'Value set in funcA'"
    $funcAVar1 = "Value set in funcA"
    funcB
}

function funcB {
    "In funcB before set -> '$funcAVar1'"
    $private:funcAVar1 = "Locally overwrite the value - child scopes can't see me!"
    "In funcB after set  -> '$funcAVar1'"
    funcC
}

function funcC {
    "In funcC before set -> '$funcAVar1' - should be the value set in funcA"
    $funcAVar1 = "Value set in funcC - Child scopes can see this change."
    "In funcC after set  -> '$funcAVar1'"
    funcD
}

function funcD {
    "In funcD before set -> '$funcAVar1' - should be the value from funcC."
    $funcAVar1 = "Value set in funcD"
    "In funcD after set  -> '$funcAVar1'"
    '-------------------'
    ShowScopes
}

function ShowScopes {
    $funcAVar1 = "Value set in ShowScopes"
    "Scope [0] (local)  `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 0 -ValueOnly)'"
    "Scope [1] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 1 -ValueOnly)'"
    "Scope [2] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 2 -ValueOnly)'"
    "Scope [3] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 3 -ValueOnly)'"
    "Scope [4] (parent) `$funcAVar1 = '$(Get-Variable funcAVar1 -Scope 4 -ValueOnly)'"
}
funcA
# End of ScopeExample.ps1
PS> .\ScopeExample.ps1

輸出會顯示每個範圍中變數的值。 您可以看到私用變數只會顯示在 中 funcB,它是在其中建立的範圍。

Setting $funcAVar1 to 'Value set in funcA'
In funcB before set -> 'Value set in funcA'
In funcB after set  -> 'Locally overwrite the value - child scopes can't see me!'
In funcC before set -> 'Value set in funcA' - should be the value set in funcA
In funcC after set  -> 'Value set in funcC - Child scopes can see this change.'
In funcD before set -> 'Value set in funcC - Child scopes can see this change.' - should be the value from funcC.
In funcD after set  -> 'Value set in funcD'
-------------------
Scope [0] (local)  $funcAVar1 = 'Value set in ShowScopes'
Scope [1] (parent) $funcAVar1 = 'Value set in funcD'
Scope [2] (parent) $funcAVar1 = 'Value set in funcC - Child scopes can see this change.'
Scope [3] (parent) $funcAVar1 = 'Locally overwrite the value - child scopes can't see me!'
Scope [4] (parent) $funcAVar1 = 'Value set in funcA'

如 的 ShowScopes輸出所示,您可以使用 並指定範圍號碼,從其他範圍 Get-Variable 存取變數。

範例 5:在遠端命令中使用局部變數

針對在本機會話中建立之遠端命令中的變數,請使用 using 範圍修飾詞。 PowerShell 假設遠端命令中的變數是在遠端會話中建立的。

語法為:

$using:<VariableName>

例如,下列命令會在本機會話中建立 $Cred 變數,然後在遠端命令中使用 $Cred 變數:

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

範圍 using 修飾詞是在 PowerShell 3.0 中引進的。

另請參閱