關於範圍
簡短描述
說明 PowerShell 中的範圍概念,並示範如何設定和變更元素的範圍。
完整描述
PowerShell 藉由限制可讀取和變更變數、別名、函式和 PowerShell 磁碟驅動器, (PSDrives) 保護其存取權。 PowerShell 會使用範圍規則,以確保您不會不小心變更不應變更的專案。
以下是範圍的基本規則:
範圍可能會巢狀。 外部範圍稱為父範圍。 任何巢狀範圍都是該父系的子範圍。
除非明確將其設為私用,否則項目會顯示在建立所在的範圍和任何子範圍中。 您可以將變數、別名、函式或 PowerShell 磁碟驅動器放在一或多個範圍內。
除非您明確指定不同的範圍,否則您在範圍內建立的專案只能變更其建立範圍。
如果您在範圍中建立專案,且專案與不同範圍中的專案共用其名稱,原始專案可能會隱藏在新專案下,但不會被覆寫或變更。
PowerShell 範圍
PowerShell 支援下列範圍:
全域:PowerShell 啟動時或建立新會話或 Runspace 時生效的範圍。 在全域範圍中建立 PowerShell 啟動時所存在的變數和函式,例如自動變數和喜好設定變數。 PowerShell 配置檔中的變數、別名和函式也會在全域範圍中建立。 全域範圍是會話中的根父範圍。
本機:目前的範圍。 本機範圍可以是全域範圍或任何其他範圍。
腳本:腳本檔案執行時所建立的範圍。 只有文稿中的命令會在文稿範圍中執行。 針對腳本中的命令,腳本範圍是本機範圍。
注意
Private 不是範圍。 這是一 個選項 ,可變更定義專案範圍外之項目的可見度。
父系和子範圍
您可以藉由呼叫文稿或函式來建立新的子範圍。 呼叫範圍是父範圍。 呼叫的腳本或函式是子範圍。 您呼叫的函式或腳本可能會呼叫其他函式,以建立子範圍的階層,其根範圍是全域範圍。
除非您明確將項目設為私用,否則父範圍中的專案可供子範圍使用。 不過,除非您在建立項目時明確指定範圍,否則您在子範圍中建立和變更的專案不會影響父範圍。
注意
來自模組的函式不會在呼叫範圍的子範圍中執行。 模組有自己的會話狀態,且已連結至全域範圍。 所有模組程式代碼都會在具有本身根範圍的模組特定範圍階層中執行。
繼承
子範圍不會從父範圍繼承變數、別名和函式。 除非專案是私用的,否則子範圍可以檢視父範圍中的專案。 此外,它可以藉由明確指定父範圍來變更專案,但專案不是子範圍的一部分。
不過,子範圍是使用一組專案所建立。 一般而言,它包含具有 AllScope 選項的所有別名。 本文稍後會討論此選項。 它包含具有 AllScope 選項的所有變數,以及一些自動變數。
若要尋找特定範圍中的專案,請使用 或Get-Alias
的 Get-Variable
Scope 參數。
例如,若要取得本機範圍中的所有變數,請輸入:
Get-Variable -Scope local
若要取得全域範圍中的所有變數,請輸入:
Get-Variable -Scope global
範圍修飾詞
變數、別名或函式名稱可以包含下列任一選擇性範圍修飾詞:
global:
- 指定名稱存在於 全域 範圍中。local:
- 指定名稱存在於 本機 範圍中。 目前的範圍一律是 本機 範圍。private:
- 指定名稱為 Private ,且只有目前範圍可見。script:
- 指定 名稱存在於文稿 範圍中。 腳本 範圍是最接近的上階腳本檔案範圍,如果沒有最接近的上階腳本檔案,則為 Global 。using:
- 用來存取另一個範圍中定義的變數,同時透過 和Invoke-Command
之類的Start-Job
Cmdlet 執行腳本。workflow:
- 指定名稱存在於工作流程中。 注意:PowerShell Core 不支援工作流程。<variable-namespace>
- PowerShell PSDrive 提供者所建立的修飾詞。 例如:命名空間 描述 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:
範圍修飾元
使用 是特殊的範圍修飾詞,可識別遠端命令中的局部變數。 如果沒有修飾詞,PowerShell 預期遠端命令中的變數會在遠端會話中定義。
範圍 Using
修飾詞是在 PowerShell 3.0 中引進。
對於執行會話外的任何腳本或命令,您需要 Using
範圍修飾詞,才能從呼叫會話範圍內嵌變數值,讓會話程式代碼無法存取它們。 下列 Using
內容支援範圍修飾詞:
- 遠端執行的命令,使用 ComputerName、HostName、SSHConnection 或 Session 參數啟動
Invoke-Command
, (遠端工作階段) - 背景工作,從
Start-Job
(跨進程會話開始) - 透過
Start-ThreadJob
個別線程會話 (啟動的線程作業)
根據內容而定,內嵌變數值是呼叫端範圍中數據的獨立複本或參考。 在遠端和跨進程會話中,它們一律是獨立的複本。
如需詳細資訊,請參閱 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
若要尋找在特定範圍中可見的變數,請使用 Scope
的 Get-Variable
參數。 可見變數包括全域變數、父範圍中的變數,以及目前範圍中的變數。
例如,下列命令會取得本機範圍中可見的變數:
Get-Variable -Scope local
若要在特定範圍中建立變數,請使用的範圍修飾詞或 的Set-Variable
Scope 參數。 下列命令會在全域範圍中建立變數:
New-Variable -Scope global -Name a -Value "One"
您也可以使用、 Set-Alias
或 Get-Alias
Cmdlet 的 New-Alias
Scope 參數來指定範圍。 下列命令會在全域範圍中建立別名:
New-Alias -Scope global -Name np -Value Notepad.exe
若要在特定範圍中取得函式, Get-Item
請在範圍中使用 Cmdlet。 Cmdlet Get-Item
沒有 Scope 參數。
注意
對於使用 Scope 參數的 Cmdlet,您也可以依編號參照範圍。 此數位描述某個範圍到另一個範圍的相對位置。 範圍 0 代表目前或本機範圍。 範圍 1 表示立即父範圍。 範圍 2 表示父範圍的父系,依此類故。 如果您已建立許多遞歸範圍,編號範圍就很有用。
搭配範圍使用點來源表示法
腳本和函式會遵循範圍的所有規則。 您會在特定範圍中建立它們,而且它們只會影響該範圍,除非您使用 Cmdlet 參數或範圍修飾詞來變更該範圍。
但是,您可以使用點來源表示法,將腳本或函式新增至目前的範圍。 然後,當腳本在目前範圍中執行時,腳本所建立的任何函式、別名和變數都可在目前範圍內使用。
若要將函式新增至目前的範圍,請在函數調用中,於函式路徑和名稱前面輸入點 (.) 和空格。
例如,若要從腳本範圍中的 C:\Scripts 目錄執行 Sample.ps1 腳本, (腳本) 的預設值,請使用下列命令:
c:\scripts\sample.ps1
若要在本機範圍中執行 Sample.ps1 腳稿,請使用下列命令:
. c:\scripts.sample.ps1
當您使用呼叫運算子 (&) 來執行函式或腳本時,它不會新增至目前的範圍。 下列範例使用呼叫運算子:
& c:\scripts.sample.ps1
您可以在 about_operators中深入瞭解呼叫運算子。
Sample.ps1 腳本建立的任何別名、函式或變數都無法在目前範圍內使用。
限制沒有範圍
一些 PowerShell 概念類似於範圍或與範圍互動。 這些概念可能會與範圍或範圍的行為混淆。
會話、模組和巢狀提示是獨立的環境,但它們不是會話中全域範圍的子範圍。
工作階段
會話是 PowerShell 執行的環境。 當您在遠端電腦上建立會話時,PowerShell 會建立與遠端電腦的持續性連線。 持續性連線可讓您針對多個相關命令使用會話。
因為會話是自主環境,所以它有自己的範圍,但會話不是建立會話的子範圍。 會話會從自己的全域範圍開始。 此範圍與會話的全域範圍無關。 您可以在工作階段中建立子範圍。 例如,您可以執行腳本,在會話中建立子範圍。
單元
您可以使用 PowerShell 模組來共享和傳遞 PowerShell 工具。 模組是可包含 Cmdlet、腳本、函式、變數、別名和其他實用專案的單元。 除非明確定義,否則無法在模組外部存取模組中的專案。 因此,您可以將模組新增至您的會話,並使用公用專案,而不必擔心其他專案可能會覆寫會話中的 Cmdlet、腳本、函式和其他專案。
根據預設,模組會載入目前 會話狀態 的最上層,而不是目前的 範圍。 目前的工作階段狀態可以是模組會話狀態或全域會話狀態。 將模組新增至會話並不會變更範圍。 如果您位於全域範圍,則會將模組載入全域會話狀態。 任何匯出都放在全域數據表中。
如果您從module1 內 載入module2,則module2會載入module1的工作階段狀態,而不是全域會話狀態。 來自module2的任何導出都位於module1工作階段狀態的頂端。 如果您使用 Import-Module -Scope local
,則匯出會放入目前的範圍物件中,而不是放在最上層。 如果您是 在模組中 ,並使用 Import-Module -Scope global
(或 Import-Module -Global
) 載入另一個模組,該模組和其導出會載入全域會話狀態,而不是模組的本機會話狀態。 這項功能是專為撰寫操作模組的模組所設計。 WindowsCompatibility 模組會執行此動作,以將 Proxy 模組匯入全域會話狀態。
在工作階段狀態中,模組有自己的範圍。 請考慮下列課程模組 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
巢狀提示
巢狀提示沒有自己的範圍。 當您輸入巢狀提示時,巢狀提示是環境的子集。 但是,您會保留在本機範圍內。
腳本確實有自己的範圍。 如果您要偵錯腳本,而且您在腳本中到達斷點,則會輸入腳本範圍。
私人選項
別名和變數具有 Option 屬性,可接受 Private 的值。 具有 [私人 ] 選項的專案可以在建立所在的範圍中檢視和變更,但無法檢視或變更該範圍以外的專案。
例如,如果您在全域範圍中建立具有私用選項的變數, Get-Variable
然後執行腳本中的命令不會顯示私用變數。 在此實例中使用全域範圍修飾詞不會顯示私用變數。
您可以使用 、 Set-Variable
New-Alias
和 Set-Alias
Cmdlet 的 New-Variable
Option 參數,將 Option 屬性的值設定為 Private。
可見度
變數或別名的 Visibility 屬性會決定您是否可以看到容器外部的專案,而該專案是建立所在的容器。 容器可以是模組、腳本或嵌入式管理單元。 可見度是針對容器所設計,其方式與Option屬性的 Private 值針對範圍所設計的方式相同。
Visibility 屬性會採用 Public 和 Private 值。 只有建立私人可見度的容器中,才能檢視和變更具有私用可見性的專案。 如果新增或匯入容器,則無法檢視或變更具有私用可見性的專案。
因為可見度是針對容器所設計,所以在範圍中的運作方式不同。
- 如果您在全域範圍中建立具有私用可見性的專案,則無法檢視或變更任何範圍中的專案。
- 如果您嘗試檢視或變更具有私用可見性的變數值,PowerShell 會傳回錯誤訊息。
您可以使用 New-Variable
和 Set-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 的變數。 私 用變數是由子範圍繼承,但只能在建立它們的範圍中檢視或變更。
下列命令會在本機範圍中建立名為 $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}
使用範圍是在 PowerShell 3.0 中引進。 在 PowerShell 2.0 中,若要指出已在本機會話中建立變數,請使用下列命令格式。
$Cred = Get-Credential
Invoke-Command $s {
param($c)
Remove-Item .\Test*.ps1 -Credential $c
} -ArgumentList $Cred