7

如何完美解決 PowerShell 無法正確解析命令列參數的問題

 2 years ago
source link: https://blog.miniasp.com/post/2022/02/12/PowerShell-argument-passing-issue-with-PSNativeCommandArgumentPassing
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

如何完美解決 PowerShell 無法正確解析命令列參數的問題

這十幾年來,在 Windows 使用 PowerShell 呼叫一些命令列工具,一直都存在一個惱人的問題,那就是傳入 *.exe 的參數會自動將雙引號(")過濾掉,導致程式無法正確執行。這個問題終於在 PowerShell v7.2.0 得到了解決,這篇文章我就來說說這個問題的來龍去脈。

我如果在 Bash 底下執行 curl 命令,要發出一個 POST 命令的話,我們會這樣執行:

curl -X POST https://localhost:7255/signin -H 'Content-Type: application/json' --data '{"Username": "will", "Password": "123"}'

因為 Windows 也有內建 cURL 命令列工具,因此我們可以在 PowerShell 底下執行完全一樣的命令,不過我們的 --data 傳入的參數,將不會是 {"Username": "will", "Password": "123"},而是 {Username: will, Password: 123},因此你的 curl 命令永遠無法執行成功!光是這一點,不知道浪費了多少人的時間啊!

我們可以透過 UnxUtils 工具組中的 echo.exe 來測試這個問題:

  1. 先透過 Chocolatey 下載安裝 UnxUtils 套件

    choco install unxutils -y
    
  2. 然後執行以下命令

    echo.exe '{"Username": "will", "Password": "123"}'
    

    此時你就會看到以下輸出:

    {Username: will, Password: 123}
    

對一個不知道此地雷的人來說,你應該永遠想不到該怎樣執行才正確!正確的執行命令如下:

echo.exe '{""""Username"""": """"will"""", """"Password"""": """"123""""}'

輸出如下:

{"Username": "will", "Password": "123"}

是不是非常意外?你要把 1 個 " 改寫成 4 個 " 才能正確的傳入 echo.exe 執行!

另一種可行的方案是以下這種格式,不過一樣不漂亮、不直覺:

echo.exe '{\"Username\": \"will\", \"Password\": \"123\"}'

輸出如下:

{"Username": "will", "Password": "123"}

從 PowerShell 7.2.0 開始,有一個實驗性的 PSNativeCommandArgumentPassing 特性 ,這個特性需要特別啟用才會生效!

以下是測試該特性的步驟:

  1. 啟用 PSNativeCommandArgumentPassing 特性

    Enable-ExperimentalFeature PSNativeCommandArgumentPassing
    
  2. 請務必重開 PowerShell 才能讓此特性生效

  3. 在參數列處理雙引號的部分就可以正常的解析了

    echo.exe '{"Username": "will", "Password": "123"}'
    

    輸出如下:

    {"Username": "will", "Password": "123"}
    

注意相容性問題

啟用 PSNativeCommandArgumentPassing 特性之後,非常有可能會讓一些早期寫好的 PowerShell 在執行命令時發生錯誤,因為他會改變你 PowerShell 處理命令列參數的行為! 🔥

這個時候你肯定要認識這個 $PSNativeCommandArgumentPassing 變數,他可以讓你執行 PowerShell 的時候彈性的切換不同的命令列參數處理方式。不過 $PSNativeCommandArgumentPassing 變數講起來有點小複雜,基本上 $PSNativeCommandArgumentPassing 變數可以有三種可能的值:

  1. 'Legacy'

    $PSNativeCommandArgumentPassing = 'Legacy'
    

    這個設定會讓 PowerShell 回復到沒有啟用該特性的時候,確保早期的 PowerShell 程式一定可以正確執行!

  2. 'Standard'

    這個是 Linux / macOS 作業系統下的預設值。

    $PSNativeCommandArgumentPassing = 'Standard'
    

    這個設定會讓 PowerShell 採用新的特性來解析命令列參數!

  3. 'Windows'

    這個是 Windows 作業系統下的預設值。

    $PSNativeCommandArgumentPassing = 'Windows'
    

    設定成這個值會讓你在執行以下命令時,採用 Legacy 模式解析命令列參數,任何其他執行檔則會使用 Standard 模式來解析,以確保最大的相容性!

    • cmd.exe
    • cscript.exe
    • wscript.exe
    • 任何副檔名為 .bat 的批次檔
    • 任何副檔名為 .cmd 的批次檔
    • 任何副檔名為 .vbs 的 VBScript 腳本

由於 PowerShell 新版是跨平台的命令列工具,該特性在不同的作業系統,預設值也是不同的!

  • Windows

    $PSNativeCommandArgumentPassing 的預設值為 'Windows'

  • non-Windows

    $PSNativeCommandArgumentPassing 的預設值為 'Standard'


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK