簡短描述
描述 PowerShell 如何剖析命令。
完整描述
當您在命令提示字元中輸入命令時,PowerShell 會將命令文字分成一系列稱為 標記的區段,然後決定如何解譯每個令牌。
例如,如果您輸入:
Write-Host book
PowerShell 會將命令分成兩個令牌,Write-Host 和 book,並使用兩個主要剖析模式之一獨立解譯每個令牌:表達式模式和自變數模式。
備註
當 PowerShell 剖析命令輸入時,它會嘗試將命令名稱解析為 Cmdlet 或原生可執行檔。 如果命令名稱沒有精確匹配,PowerShell 會在命令前面加上 Get- 作為預設動詞。 例如,PowerShell 會將 Service 剖析為 Get-Service。 基於下列原因,不建議使用此功能:
- 效率不佳。 這會導致PowerShell多次搜尋。
- 具有相同名稱的外部程式會先解析,因此您可能不會執行預期的 Cmdlet。
-
Get-Help和Get-Command無法辨識無動詞名稱。 - 命令名稱可以是保留字或語言關鍵詞。
Process為 ,且無法解析為Get-Process。
表達式模式
表達式模式是用於結合運算式,這是在腳本語言中進行值操作的必要功能。 表達式是PowerShell語法中值的表示法,而且可以是簡單或複合的,例如:
字面表達式是其值的直接表示法:
'hello'
32
變數表示式會包含它們所參考之變數的值:
$x
$script:path
運算子將其他運算式結合起來以進行計算:
-12
-not $Quiet
3 + 7
$input.Length -gt 1
- 字串常值 必須包含在引號中。
- 數字 會被視為數值,而不是一系列字元(除非轉義)。
-
運算子,包括一元運算符,例如
-和-not,以及+和-gt等二元運算符,都會解譯為運算符,並在其自變數上套用各自的運算(操作數)。 -
屬性和轉換表達式 會剖析為表達式,並套用至次級表達式。 例如:
[int] '7'。 - 變數參考 會評估為其值,但禁止 解構,並造成剖析器錯誤。
- 任何其他事物都會被視為要執行的命令。
自變數模式
剖析時,PowerShell 會先將輸入解譯為表達式。 但是當遇到命令調用時,解析會繼續在參數模式下進行。 如果您有包含空格的自變數,例如路徑,則必須以引號括住這些自變數值。
自變數模式是針對殼層環境中的命令剖析自變數和參數所設計。 除非輸入使用下列其中一個語法,否則所有輸入都會被視為可展開的字串:
美元符號(
$)後接變數名稱時,開始形成變數參考;否則,它會被解析為可展開字串的一部分。 變數參考可以包含成員存取或索引。- 在簡單變數參考之後的其他字元,例如
$HOME,會被視為相同參數的一部分。 將變數名稱括在大括號 ({}) 中,將它與後續字元分開。 例如:${HOME}。 - 當變數參考包含成員存取權時,任何額外字元中的第一個都會被視為新自變數的開頭。 例如,
$HOME.Length-more會產生兩個自變數:$HOME.Length的值和字串常值-more。
- 在簡單變數參考之後的其他字元,例如
字串以引號(
'和")開始大括號(
{})開啟新的腳本區塊逗號 (
,) 導入做為陣列傳遞的清單,除非所呼叫的命令是原生應用程式,在此情況下,它們會解譯為可展開字串的一部分。 不支援初始、連續或尾端逗號。括弧 (
()) 開始新的表達式子表達式運算子(
$())會開啟嵌入表達式初始@符號(
@)開始表示式語法,例如splatting(@args)、陣列(@(1,2,3))及哈希表常值(@{a=1;b=2})。在令牌的開頭若有
()、$()和@(),則會建立一個可包含表達式或巢狀命令的新解析上下文。- 後面接著其他字元時,第一個額外的字元會被視為新的個別自變數的開頭。
- 在前面加上無引號常值
$()時,其運作方式就像可展開的字串;()會啟動新的以表達式為形式的參數,而@()會被視為常值@,()會啟動新的以表達式為形式的參數。
除了仍然需要轉義的元字符之外,其他所有內容都會被視為可展開的字串。 請參閱 處理特殊字元。
- 自變數模式元字元(具有特殊語法意義的字元)是:
<space> ' " ` , ; ( ) { } | & < > @ #。 在這些中,< > @ #只有在標記開頭才具有特別性。
- 自變數模式元字元(具有特殊語法意義的字元)是:
範例
下表提供數個在表達式模式和自變數模式中處理的令牌範例,以及這些令牌的評估。 在這些範例中,變數 $a 的值是 4。
| 範例 | 模式 | 結果 |
|---|---|---|
2 |
表現 | 2 (整數) |
`2 |
表現 | “2” (命令) |
Write-Output 2 |
表現 | 2 (整數) |
2+2 |
表現 | 4 (整數) |
Write-Output 2+2 |
論點 | “2+2” (字串) |
Write-Output(2+2) |
表現 | 4 (整數) |
$a |
表現 | 4 (整數) |
Write-Output $a |
表現 | 4 (整數) |
$a+2 |
表現 | 6 (整數) |
Write-Output $a+2 |
論點 | “4+2” (字串) |
$- |
論點 | “$-” (指令) |
Write-Output $- |
論點 | “$-” (string) |
a$a |
表現 | “a$a” (命令) |
Write-Output a$a |
論點 | “a4” (字串) |
a'$a' |
表現 | “a$a” (命令) |
Write-Output a'$a' |
論點 | “a$a” (字符串) |
a"$a" |
表現 | “a$a” (命令) |
Write-Output a"$a" |
論點 | “a4” (字串) |
a$(2) |
表現 | “a$(2)” (命令) |
Write-Output a$(2) |
論點 | “a2” (字串) |
每個權杖都可以解釋為某類物件型別,例如 布爾值 或 字串。 PowerShell 會嘗試從表達式判斷物件類型。 物件類型取決於命令預期的參數類型,以及 PowerShell 是否知道如何將自變數轉換成正確的類型。 下表顯示指派給表達式所傳回值的數個型別範例。
| 範例 | 模式 | 結果 |
|---|---|---|
Write-Output !1 |
引數 | “!1” (字串) |
Write-Output (!1) |
運算式 | 假(布爾值) |
Write-Output (2) |
運算式 | 2 (整數) |
Set-Variable AB A,B |
引數 | 'A','B'(陣列) |
CMD /CECHO A,B |
引數 | 'A,B' (字串) |
CMD /CECHO $AB |
運算式 | 'A B' (陣列) |
CMD /CECHO :$AB |
引數 | ':A B' (字串) |
處理特殊字元
反引號字元 (`) 可用來逸出表達式中的任何特殊字元。 這最適用於將參數模式元字符轉義為常值字元而非元字符使用。 例如,若要使用貨幣符號($)作為可展開字串中的字面值:
"The value of `$ErrorActionPreference is '$ErrorActionPreference'."
The value of $ErrorActionPreference is 'Continue'.
線條接續
反引號字元也可以在一行的結尾使用,以便讓您能夠在下一行繼續輸入。 這可改善命令的可讀性,該命令會採用數個具有長名稱和自變數值的參數。 例如:
New-AzVm `
-ResourceGroupName "myResourceGroupVM" `
-Name "myVM" `
-Location "EastUS" `
-VirtualNetworkName "myVnet" `
-SubnetName "mySubnet" `
-SecurityGroupName "myNetworkSecurityGroup" `
-PublicIpAddressName "myPublicIpAddress" `
-Credential $cred
不過,您應該避免使用行接續。
- 反引號字元可能不易看到,也容易被遺忘。
- 反引號后的額外空間會中斷行接續。 由於空間很難看到,所以很難找到錯誤。
PowerShell 提供數種方法在語法中自然地換行。
- 管線字元之後 (
|) - 二元運算符之後(
+、-、-eq等) - 在陣列中逗號之後 (
,) - 開啟
[、{、(等字元之後
針對大型參數集,請改用散射方法。 例如:
$parameters = @{
ResourceGroupName = "myResourceGroupVM"
Name = "myVM"
Location = "EastUS"
VirtualNetworkName = "myVnet"
SubnetName = "mySubnet"
SecurityGroupName = "myNetworkSecurityGroup"
PublicIpAddressName = "myPublicIpAddress"
Credential = $cred
}
New-AzVm @parameters
將參數傳遞至原生指令
從 PowerShell 執行原生命令時,PowerShell 會先剖析自變數。 剖析的自變數接著會聯結成單一字串,每個參數都以空格分隔。
例如,下列命令會呼叫 icacls.exe 程式。
icacls X:\VMS /grant Dom\HVAdmin:(CI)(OI)F
若要在 PowerShell 2.0 中執行此命令,您必須使用逸出字元來防止 PowerShell 錯誤解譯括號。
icacls X:\VMS /grant Dom\HVAdmin:`(CI`)`(OI`)F
停止解析令牌
從 PowerShell 3.0 開始,您可以使用 停止解析 (--%) 令牌來防止 PowerShell 將輸入解析為命令或表達式。
備註
停止解析符號僅適用於 Windows 平臺上的本機命令。
呼叫原生命令時,請將停止剖析令牌放在程式參數之前。 這項技術比使用逸出字元來避免誤解要容易得多。
當遇到停止解析符號時,PowerShell 會將行中的其餘字元視為文字。 它執行的唯一解譯是替代使用標準 Windows 表示法的環境變數值,例如 %USERPROFILE%。
icacls X:\VMS --% /grant Dom\HVAdmin:(CI)(OI)F
PowerShell 會將下列命令字串傳送至 icacls.exe 程式:
X:\VMS /grant Dom\HVAdmin:(CI)(OI)F
停止剖析令牌只有在遇到下一個換行符或管線字元之前才有效。 您無法使用行接續字元 (`) 來擴充其效果,或使用命令分隔符 (;) 來終止其效果。
除了 %variable% 環境變數參考之外,您無法在命令中內嵌任何其他動態元素。 不支援在批處理檔中使用將 % 字元跳脫為 %%的方式。
%<name>% 代碼標籤總是展開。 如果 <name> 未參考定義的環境變數,令牌會透過 as-is傳遞。
您無法使用流重定向(例如 >file.txt),因為它們會原樣傳遞作為目標命令的參數。
在下列範例中,第一個步驟會執行命令,並不使用停止剖析符號。 PowerShell 會評估引號字串,並將值 (不含引號) 傳遞至 cmd.exe,這會導致錯誤。
PS> cmd /c echo "a|b"
'b' is not recognized as an internal or external command,
operable program or batch file.
PS> cmd /c --% echo "a|b"
"a|b"
備註
使用 PowerShell Cmdlet 時,不需要停止解析標記。 不過,將自變數傳遞至PowerShell函式可能很有用,其設計目的是使用這些自變數呼叫原生命令。
傳遞包含引號字元的參數
某些原生命令需要包含引號字元的自變數。 PowerShell 7.3 已變更命令行剖析原生命令的方式。
謹慎
新行為是 Windows PowerShell 5.1 行為的 重大更改 。 這可能會中斷在叫用原生應用程式時解決各種問題的腳本和自動化。 在需要時,使用停止剖析標記(--%)或 Start-Process cmdlet 以避免本機引數傳遞。
新的 $PSNativeCommandArgumentPassing 喜好設定變數會控制此行為。 此變數可讓您在運行時間選取行為。 有效值為 Legacy、Standard和 Windows。 默認行為是平臺特定的。 在 Windows 平臺上,預設設定為 Windows,非 Windows 平台預設為 Standard。
Legacy 是具有歷史意義的行為。
Windows 和 Standard 模式的行為相同,但在 Windows 模式中,下列檔案的調用會自動使用 Legacy 樣式自變數傳遞。
cmd.execscript.exewscript.exe- 以
.bat結尾 - 以
.cmd結尾 - 以
.js結尾 - 以
.vbs結尾 - 以
.wsf結尾
如果 $PSNativeCommandArgumentPassing 設定為 Legacy 或 Standard,剖析器不會檢查這些檔案。
備註
下列範例使用 TestExe.exe 工具。 您可以從原始程式碼建置 TestExe。 請參閱 PowerShell 來源存放庫中的 TestExe。
這項變更帶來的新行為:
具有內嵌引號的常值或可展開字串,現在引號會被保留。
PS> $a = 'a" "b' PS> TestExe -echoargs $a 'c" "d' e" "f Arg 0 is <a" "b> Arg 1 is <c" "d> Arg 2 is <e f>空字串作為參數現在會保留:
PS> TestExe -echoargs '' a b '' Arg 0 is <> Arg 1 is <a> Arg 2 is <b> Arg 3 is <>
這些範例的目標是將目錄路徑 (含空格和引號) "C:\Program Files (x86)\Microsoft\" 傳遞至原生命令,使其接收路徑做為引號字串。
在 Windows 或 Standard 模式中,下列範例會產生預期的結果:
TestExe -echoargs """${env:ProgramFiles(x86)}\Microsoft\"""
TestExe -echoargs '"C:\Program Files (x86)\Microsoft\"'
若要在 Legacy 模式中取得相同的結果,您必須跳脫引號或使用停止解析符(--%):
TestExe -echoargs """""${env:ProgramFiles(x86)}\Microsoft\\"""""
TestExe -echoargs "\""C:\Program Files (x86)\Microsoft\\"""
TestExe -echoargs --% ""\""C:\Program Files (x86)\Microsoft\\"\"""
TestExe -echoargs --% """C:\Program Files (x86)\Microsoft\\""
TestExe -echoargs --% """%ProgramFiles(x86)%\Microsoft\\""
備註
PowerShell 無法將反斜杠 (\) 字元辨識為逸出字元。 這是基礎 API 用於 ProcessStartInfo.ArgumentList的逸出字元。
PowerShell 7.3 也新增了追蹤原生命令的參數系結的功能。 如需詳細資訊,請參閱 Trace-Command。
將自變數傳遞至 PowerShell 命令
從 PowerShell 3.0 開始,您可以使用 端參數 令牌 (--) 來停止 PowerShell 將輸入解譯為 PowerShell 參數。 這是 POSIX 殼層和公用程式規格中指定的慣例。
參數結尾標誌
參數結尾標記 (--) 表示之後的所有自變數都會以實際形式傳遞,就像將雙引號放在它們周圍一樣。 例如,使用 -- 您可以輸出字串 -InputObject,而不使用引號或將其解譯為參數:
Write-Output -- -InputObject
-InputObject
不同於停止剖析(--%)令牌,-- 令牌後面的任何值都可以由 PowerShell 解譯為表達式。
Write-Output -- -InputObject $env:PROCESSOR_ARCHITECTURE
-InputObject
AMD64
此行為僅適用於PowerShell命令。 如果您在呼叫外部命令時使用 -- 令牌,-- 字串會當做自變數傳遞給該命令。
TestExe -echoargs -a -b -- -c
輸出顯示 -- 會當做自變數傳遞至 TestExe。
Arg 0 is <-a>
Arg 1 is <-b>
Arg 2 is <-->
Arg 3 is <-c>
蒂爾德 (~)
波浪符號(~)在 PowerShell 中具有特殊意義。 當它與路徑開頭的PowerShell命令搭配使用時,磚字元會展開至使用者的主目錄。 如果路徑中任何其他位置都使用Tilde 字元,則會將其視為常值字元。
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Set-Location ~
PS C:\Users\user2> $PWD
Path
----
C:\Users\user2
在此範例中, 的 New-Item 參數需要字串。 波浪號字元會被視為文字字元。 若要變更為新建立的目錄,您必須使用並排字元來限定路徑。
PS D:\temp> Set-Location ~
PS C:\Users\user2> New-Item -Type Directory -Name ~
Directory: C:\Users\user2
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 5/6/2024 2:08 PM ~
PS C:\Users\user2> Set-Location ~
PS C:\Users\user2> Set-Location .\~
PS C:\Users\user2\~> $PWD
Path
----
C:\Users\user2\~
當您搭配原生命令使用 tilde 字元時,PowerShell 會將磚傳遞為常值字元。 在路徑中使用Tilde 會導致 Windows 上不支援Tilde 字元的原生命令發生錯誤。
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Get-Item ~\repocache.clixml
Directory: C:\Users\user2
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 4/29/2024 3:42 PM 88177 repocache.clixml
PS D:\temp> more.com ~\repocache.clixml
Cannot access file D:\temp\~\repocache.clixml