簡短描述
說明 PowerShell 中的範圍概念,並示範如何設定和變更元素的範圍。
完整描述
PowerShell 會限制可以讀取和變更變數、別名、函式和 PowerShell 磁碟驅動器 (PSDrives) 的存取。 PowerShell 使用範圍規則來確保不會無意中更改不應更改的項。
以下是範圍的基本規則:
範圍可能會巢狀。 外部範圍稱為父範圍。 任何嵌套範疇都是該父範疇的子範疇。
項在創建它的作用域和任何子作用域中都可見,除非您明確將其設為私有。
您可以在當前範圍之外的範圍內聲明變數、別名、函數和 PowerShell 驅動器。
除非您明確指定不同的範疇,否則您在某個範疇內建立的項目只能在該建立範疇中變更。
如果在範圍內創建項,並且該項與其他範圍內的項共用其名稱,則原始項可能會隱藏在新項下,但不會被覆蓋或更改。
PowerShell 範圍
PowerShell 支援下列範圍:
Global(全域):P owerShell 啟動時或創建新會話或運行空間時生效的範圍。 PowerShell 啟動時存在的變數和函數是在全域範圍內創建的,例如自動變數和首選項變數。 PowerShell 配置檔中的變數、別名和函式也會在全域範圍中建立。 全域範圍是會話中的根父範圍。
Local:當前範圍。 當前的範圍可以是全域範圍或任何其他範圍。
Script(文稿):在腳本檔運行時創建的範圍。 只有文本中的命令在腳本範圍內運行。 對於文稿中的命令,文本範圍是本地範圍。
備註
Private 不是一個範圍。 這是一個 選項 ,用於更改定義項的範圍之外的項的可見性。
父範圍和子範圍
您可以呼叫文稿或函式來建立新的子範圍。 呼叫範圍是父範圍。 呼叫的腳本或函式是子程序範圍。 您調用的函式或腳本可能會調用其他函式,從而形成一個以全域範圍為根範圍的子範圍層次結構。
除非您明確將項目設為私用,否則父範圍中的專案可供子範圍使用。 但是,您在子作用域中創建和更改的項不會影響父作用域,除非您在創建項時顯式指定作用域。
備註
模組中的函數不會在調用範圍的子範圍內運行。 模組有自己的會話狀態,該狀態連結到全域範圍。 所有模組程式代碼都會在具有本身根範圍的模組特定範圍階層中執行。
遺產
子範圍不會從父範圍繼承變數、別名和函數。 除非項是私有的,否則子範圍可以查看父範圍中的項。 而且,它可以通過顯式指定父範圍來更改項,但這些項不是子範圍的一部分。
但是,子範圍是使用一組項創建的。 通常,它包括具有 AllScope 選項的所有別名。 本文稍後會討論此選項。 它包括具有 AllScope 選項的所有變數,以及一些自動變數。
若要尋找特定範圍中的專案,請使用 Get-Variable 或 Get-Alias的 Scope 參數。
例如,若要取得本機範圍中的所有變數,請輸入:
Get-Variable -Scope local
若要取得全域範圍中的所有變數,請輸入:
Get-Variable -Scope global
範圍修飾符
變數、別名或函式名稱可以包含下列任一選擇性範圍修飾詞:
global:- 指定名稱存在於 全域 範圍中。local:- 指定此名稱存在於 區域 範圍內。 目前的範圍一律是 本地 範圍。private:- 指定名稱 私用,而且只能看見目前範圍。script:- 指定名稱存在於 文稿 範圍中。 腳本 範圍是最接近的上階腳本檔案的範圍,如果沒有這樣的上階腳本檔案,則為 全域。using:- 用於在透過 cmdlet 執行文稿時存取在另一個範圍中定義的變數,例如Start-Job和Invoke-Command。workflow:- 指定名稱存在於工作流程中。 注意:PowerShell v6 及更高版本不支援工作流。<variable-namespace>- 由 PowerShell PSDrive 提供程式創建的修飾符。 例如:Namespace 說明 Alias:目前範圍中定義的別名 Env:目前範圍中定義的環境變數 Function:當前範圍中定義的函式 Variable:目前範圍中定義的變數
文稿的預設範圍是腳本範圍。 函數和別名的預設範圍是本地範圍,即使它們是在腳本中定義的。
使用範圍修飾詞
若要指定新變數、別名或函式的範圍,請使用範圍修飾詞。
變數中範圍修飾詞的語法為:
$[<scope-modifier>:]<name> = <value>
函式中範圍修飾詞的語法如下:
function [<scope-modifier>:]<name> {<function-body>}
以下命令(不使用 scope 修飾符)在當前或 局部 範圍內創建變數:
$a = "one"
若要在 全域 範圍中建立相同的變數,請使用範圍 global: 修飾詞:
$global:a = "one"
若要在 文稿 範圍中建立相同的變數,請使用 script: 範圍修飾詞:
$script:a = "one"
您也可以在函式中使用範疇修飾詞。 下列函式定義會在 全域 範圍建立函式:
function global:Hello {
Write-Host "Hello, World"
}
您也可以使用範圍修飾詞來參考其他範圍中的變數。
下列命令指的是 $test 變數,首先在區域作用域中,然後在全域作用域中。
$test
$global:test
Using: 範圍修飾詞
Using 是特殊的範圍修飾詞,可識別遠端命令中的局部變數。 如果沒有修飾詞,PowerShell 預期遠端命令中的變數會在遠端會話中定義。
PowerShell 3.0 中引進了 Using 範圍修飾詞。
針對任何在會話外執行的腳本或命令,您需要使用 Using 範圍修飾詞,將呼叫會話範圍中的變數值嵌入,使得會話外的代碼可以存取這些變數。 下列情境支援 Using 範圍修飾詞:
- 遠端執行的命令已透過
Invoke-Command、HostName、SSHConnection 或 Session 參數啟動 (遠端會話) - 背景工作,從
Start-Job開始(進程外的工作階段) - 線程作業,透過
Start-ThreadJob或ForEach-Object -Parallel啟動(個別線程會話)
根據內容,內嵌變數值可以是呼叫端範圍中數據的獨立複本,或是參考數據。 在遠端和跨進程會話中,它們一律是獨立的複本。
如需詳細資訊,請參閱 about_Remote_Variables。
在執行緒會話中,物件是以引用方式傳遞的。 這意味著可以在不同的線程中修改 call scope 變數。 要安全地修改變量,需要線程同步。
如需詳細資訊,請參閱:
變數值的串行化
遠端執行的命令和背景作業在獨立進程中運行。 跨進程會話會使用以 XML 為基礎的串行化和還原串行化,讓變數的值可跨進程界限使用。 串行化程式會將物件轉換成包含原始物件屬性,但不包含其方法的 PSObject。
針對一組有限的類型,反序列化會將物件恢復為原始類型。 重新水化的對象是原始對象的複本。 它具有型別屬性和方法。 對於簡單類型,例如 System.Version,複製是精確的。 對於複雜類型,復本不完美。 例如,解除凍結的憑證物件不包含私鑰。
所有其他類型的實例都是 PSObject 的實例。 PSTypeNames 屬性包含原始類型名稱,前面加上 反序列化,例如,反序列化.System.Data.DataTable
AllScope 選項
變數和別名具有 Option 屬性,可接受 AllScope的值。 具有 AllScope 屬性的項將成為您創建的任何子範圍的一部分,儘管它們不會由父範圍追溯繼承。
具有 AllScope 屬性的項在子範圍中可見,並且它是該範圍的一部分。 變更任何範圍中的項目會影響定義變數的所有範圍。
管理範圍
數個 Cmdlet 具有 Scope 參數,可讓您取得或設定特定範圍中的專案(建立和變更)。 使用下列命令來尋找會話中具有 Scope 參數的所有 Cmdlet:
Get-Help * -Parameter scope
若要尋找在特定範圍中可見的變數,請使用 Scope的 Get-Variable 參數。 可見變數包括全域變數、父範圍中的變數,以及目前範圍中的變數。
例如,下列命令會取得區域範圍中可見的變數:
Get-Variable -Scope local
若要在特定範圍中建立變數,請使用範圍修飾詞或 Scope 參數 Set-Variable。 下列命令會在全域範圍中建立變數:
New-Variable -Scope global -Name a -Value "One"
您也可以使用 New-Alias、Set-Alias或 Get-Alias Cmdlet 的 Scope 參數來指定範圍。 下列命令會在全域範圍中建立別名:
New-Alias -Scope global -Name np -Value Notepad.exe
若要取得特定範圍中的函式,請在範圍中使用 Get-Item Cmdlet。 cmdlet Get-Item 沒有 Scope 參數。
備註
對於使用 Scope 參數的 cmdlet,您也可以使用編號來引用範圍。 此數位描述一個範圍到另一個範圍的相對位置。 範圍 0 代表當前或本機範圍。 範圍 1 表示直接父範圍。 範圍 2 表示父範圍的父代等等。 如果您已建立許多遞歸範圍,則編號範圍很有用。
將 Dot Source Notation 與 Scope 一起使用
腳本和函數遵循範圍的所有規則。 您會在特定範圍中建立它們,而且它們只會影響該範圍,除非您使用 Cmdlet 參數或範圍修飾詞來變更該範圍。
但是,您可以使用點源表示法將腳本或函數添加到當前範圍。 然後,當腳本在當前範圍內運行時,該腳本創建的任何函數、別名和變數在當前範圍內都可用。
要將函數添加到當前範圍,請在函數調用中函數的路徑和名稱前鍵入點 (.) 和空格。
例如,要從腳本範圍(腳本的預設值)中的 C:\Scripts 目錄運行 Sample.ps1 腳本,請使用以下命令:
c:\scripts\sample.ps1
要在本地範圍內運行 Sample.ps1 腳本,請使用以下命令:
. c:\scripts.sample.ps1
當您使用調用運算子 (&) 運行函數或腳本時,它不會添加到當前範圍。 以下示例使用 call 運算子:
& c:\scripts.sample.ps1
您可以在 about_operators 中閱讀有關呼叫運算子的更多資訊。
Sample.ps1 文稿創建的任何別名、函數或變數在當前範圍內不可用。
無範圍限制
一些 PowerShell 概念類似於範圍或與範圍交互。 這些概念可能會與 scope 或 scope 的行為相混淆。
會話、模組和嵌套提示是自包含環境,但它們不是會話中全域作用域的子作用域。
會議
會話是PowerShell執行所在的環境。 當您在遠端電腦上建立會話時,PowerShell 會建立與遠端電腦的持續性連線。 持續性連線可讓您針對多個相關命令使用會話。
由於會話是包含的環境,因此它有自己的範圍,但會話不是在其中創建它的會話的子範圍。 會話從其自身的全域範圍開始。 此範圍與會話的全域範圍無關。 您可以在工作階段中建立子範圍。 例如,您可以執行腳本,在會話中建立子範圍。
模組
您可以使用 PowerShell 模組來共享和傳遞 PowerShell 工具。 模組是一個單位,可以包含 Cmdlet、腳本、函式、變數、別名和其他實用專案。 除非明確定義,否則模組中的項不能在模組外部訪問。 因此,您可以將模組新增至您的會話,並使用公用項目,而不必擔心其他項目可能會覆蓋您會話中的 Cmdlet、腳本、函式及其他項目。
默認情況下,模組被載入到當前 工作階段狀態 的頂層,而不是當前 範圍。 當前工作階段狀態可以是模組會話狀態或全域會話狀態。 將模組添加到會話不會更改範圍。 如果你在全域範圍內,那麼模組將被載入到全域會話狀態中。 任何匯出都將放入全域表中。
如果從module1 中 載入module2,則module2將載入到module1的會話狀態中,而不是全域會話狀態中。 從module2導出的任何數據都位於module1工作階段狀態的頂部。 如果您使用 Import-Module -Scope local,則匯出的內容會放入當前的範圍物件中,而不是放在全域層級。 如果你 在一個模組中 並使用 Import-Module -Scope global (or Import-Module -Global) 載入另一個模組,則該模組及其匯出將載入到全域會話狀態中,而不是模組的本地工作階段狀態中。 此功能專為編寫作模組的模組而設計。
WindowsCompatibility 模組執行此作是為了將代理模組導入到全域會話狀態中。
在 session 狀態中,模組有自己的範圍。 請考慮下列模組 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。 具有 Private (私有 ) 選項的專案可以在創建它們的範圍內查看和更改,但不能在該範圍之外查看或更改它們。
例如,如果創建一個在全域範圍內具有 private 選項的變數,然後運行腳本, Get-Variable 則腳本中的命令不會顯示該 private 變數。 在此實例中使用 global scope 修飾符不會顯示 private 變數。
可以使用 、、 New-Variable和 Set-VariableNew-Alias cmdlet 的 Set-AliasOption 參數將 Option 屬性的值設置為 Private。
能見度
變數或別名的 Visibility 屬性會決定您是否能在該變數或別名被創建的容器外部看到此項目。 容器可以是模組、腳本或嵌入式管理單元。 Visibility 是為容器設計的,其方式與Option屬性的 Private 值是為範圍設計的相同。
Visibility 屬性採用 Public 和 Private 值。 具有私人可見性的項目只能在其創建的容器中查看和更改。 如果添加或導入容器,則無法查看或更改具有私有可見性的專案。
因為可見度是針對容器所設計,所以在範圍中的運作方式不同。
- 如果創建的項在全域範圍內具有私有可見性,則無法在任何範圍內查看或更改該項。
- 如果您嘗試檢視或變更具有私用可見性的變數值,PowerShell 會傳回錯誤訊息。
您可以使用 New-Variable 和 Set-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"
接下來,建立定義 $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:創建私有變數
私有變數是具有值為 Private 的 Option 屬性的變數。 私有 變數由子作用域繼承,但只能在創建它們的作用域中查看或更改它們。
以下命令創建一個在local範圍內調用 $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