共用方式為


about_Scopes

簡短描述

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

完整描述

PowerShell 藉由限制可讀取和變更變數、別名、函式和 PowerShell 磁碟驅動器 (PSDrives) ,以保護存取變數、別名、函式和 PowerShell 磁碟驅動器。 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 代表目前 (本機) 範圍,範圍 1 是目前範圍的父系,範圍 2 是目前範圍的父代。 此模式會繼續執行,直到您到達根範圍為止。

範圍修飾詞

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

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

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

  • private: - 指定名稱為 Private ,且只對目前範圍可見。

    注意

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

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

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

  • workflow: - 指定名稱存在於工作流程中。 注意:PowerShell v6 和更新版本中不支援工作流程。

  • <variable-namespace> - PowerShell PSDrive 提供者所建立的修飾詞。 例如:

    命名空間 描述
    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: 範圍修飾元

使用 是特殊範圍修飾詞,可識別遠端命令中的局部變數。 若沒有修飾詞,PowerShell 預期遠端命令中的變數必須在遠端會話中定義。

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

對於執行會話外的任何腳本或命令,您需要 using 範圍修飾詞,才能從呼叫會話範圍內嵌變數值,讓會話程序代碼可以存取它們。 下列 using 內容支援範圍修飾詞:

  • 從遠端執行的命令,從 Invoke-Command 使用 ComputerNameHostNameSSHConnectionSession 參數 (遠端工作階段)
  • 背景工作,從 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 -Global) 載入另一個模組Import-Module -Scope global,該模組及其匯出會載入全域範圍,而不是模組的本機範圍。 WindowsCompatibility 功能會執行這項作業,將 Proxy 模組匯入全域會話狀態。

巢狀提示

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

腳本有自己的範圍。 如果您要偵錯文本,而且您在腳本中到達斷點,請輸入腳本範圍。

私人選項

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

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

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

可見度

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

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

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

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

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

範例

範例 1:只在腳本中變更變數值

下列命令會變更腳本中變數的值 $ConfirmPreference 。 變更不會影響全域範圍。

首先,若要在本機範圍中顯示變數的值 $ConfirmPreference ,請使用下列命令:

PS>  $ConfirmPreference
High

Create 包含下列命令的 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"

接下來,建立定義變數的 Sample.ps1 腳本 $test 。 在腳本中,使用範圍修飾詞來參考變數的 $test 全域或本機版本。

在 Sample.ps1:

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

當腳本完成時,會變更的 $test 全域值。

PS> $test
Local

範例 4:建立私人變數

您可以使用範圍修飾詞,或使用Option屬性設定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 中引進。

另請參閱