about_Parsing

簡短描述

描述 PowerShell 如何剖析命令。

詳細描述

當您在命令提示字元中輸入命令時,PowerShell 會將命令文字分成一系列稱為 令牌 的區段,然後決定如何解譯每個令牌。

例如,如果您輸入:

Write-Host book

PowerShell 會將命令分成兩個標記, Write-Host 並使用 book兩個主要剖析模式之一獨立解譯每個令牌:表達式模式和自變數模式。

注意

當 PowerShell 剖析命令輸入時,它會嘗試將命令名稱解析為 Cmdlet 或原生可執行檔。 如果命令名稱沒有完全相符,PowerShell 會在命令前面加上 Get- 作為默認動詞命令。 例如,PowerShell 會剖析 ServiceGet-Service。 基於下列原因,不建議使用此功能:

  • 效率不佳。 這會導致PowerShell多次搜尋。
  • 具有相同名稱的外部程式會先解析,因此您可能不會執行預期的 Cmdlet。
  • Get-HelpGet-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的值。
  • 引號 ('") 開始字串

  • 大括弧 ({}) 開始新的腳本區塊

  • 逗號 (,) 匯入以數位形式傳遞的清單,除非所呼叫的命令是原生應用程式,在此情況下,這些清單會解譯為可展開字串的一部分。 不支援初始、連續或尾端逗號。

  • 括弧 (()) 開始新的運算式

  • Subexpression 運算子 ($()) 會開始內嵌表達式

  • 初始符號 (@) 會開始表達式語法,例如 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” (字串)
$- 引數 “$-” (command)
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) expression False (布爾值)
Write-Output (2) expression 2 (整數)
Set-Variable AB A,B 引數 'A','B' (陣列)
CMD /CECHO A,B 引數 'A,B' (字串)
CMD /CECHO $AB expression '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 將輸入解譯為 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> 參考已定義的環境變數,令牌會依預期方式傳遞。

您無法使用數據流重新導向(例如 >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 已變更命令行剖析原生命令的方式。

警告

新行為是 Window PowerShell 5.1 行為的重大變更 。 這可能會中斷在叫用原生應用程式時解決各種問題的腳本和自動化。 使用停止剖析令牌 (--%) 或 Start-Process Cmdlet,以避免在需要時傳遞原生自變數。

新的 $PSNativeCommandArgumentPassing 喜好設定變數會控制此行為。 此變數可讓您在運行時間選取行為。 合法值為 LegacyStandardWindows。 默認行為是平臺特定的。 在 Windows 平臺上,預設設定為 Windows ,非 Windows 平台預設為 Standard

Legacy 是歷史行為。 和 Standard 模式的行為Windows相同,但在 模式中Windows,下列檔案的調用會自動使用Legacy傳遞的 style 自變數。

  • cmd.exe
  • cscript.exe
  • wscript.exe
  • 結尾為 .bat
  • 結尾為 .cmd
  • 結尾為 .js
  • 結尾為 .vbs
  • 結尾為 .wsf

$PSNativeCommandArgumentPassing如果 設定為 LegacyStandard,則剖析器不會檢查這些檔案。

注意

下列範例會使用 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\" 傳遞至原生命令,使其接收路徑做為引號字串。

WindowsStandard 模式中,下列範例會產生預期的結果:

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 for 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>

另請參閱