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

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

### 重現問題

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

```bash
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](http://unxutils.sourceforge.net/) 工具組中的 `echo.exe` 來測試這個問題：

1.  先透過 Chocolatey 下載安裝 [UnxUtils](https://community.chocolatey.org/packages/unxUtils) 套件
    
    ```powershell
    choco install unxutils -y
    ```
    
2.  然後執行以下命令
    
    ```powershell
    echo.exe '{"Username": "will", "Password": "123"}'
    ```
    
    此時你就會看到以下輸出：
    
    ```
    {Username: will, Password: 123}
    ```
    

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

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

輸出如下：

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

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

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

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

輸出如下：

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

### 解決方案

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

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

1.  啟用 `PSNativeCommandArgumentPassing` 特性
    
    ```powershell
    Enable-ExperimentalFeature PSNativeCommandArgumentPassing
    ```
    
2.  請務必重開 PowerShell 才能讓此特性生效
    
3.  在參數列處理雙引號的部分就可以正常的解析了
    
    ```powershell
    echo.exe '{"Username": "will", "Password": "123"}'
    ```
    
    輸出如下：
    
    ```json
    {"Username": "will", "Password": "123"}
    ```
    

### 注意相容性問題

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

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

1.  `'Legacy'`
    
    ```powershell
    $PSNativeCommandArgumentPassing = 'Legacy'
    ```
    
    這個設定會讓 PowerShell 回復到沒有啟用該特性的時候，確保早期的 PowerShell 程式一定可以正確執行！
    
2.  `'Standard'`
    
    這個是 Linux / macOS 作業系統下的預設值。
    
    ```powershell
    $PSNativeCommandArgumentPassing = 'Standard'
    ```
    
    這個設定會讓 PowerShell 採用新的特性來解析命令列參數！
    
3.  `'Windows'`
    
    這個是 Windows 作業系統下的預設值。
    
    ```powershell
    $PSNativeCommandArgumentPassing = 'Windows'
    ```
    
    設定成這個值會讓你在執行以下命令時，採用 `Legacy` 模式解析命令列參數，任何其他執行檔則會使用 `Standard` 模式來解析，以確保最大的相容性！
    
    -   `cmd.exe`
    -   `cscript.exe`
    -   `wscript.exe`
    -   任何副檔名為 `.bat` 的批次檔
    -   任何副檔名為 `.cmd` 的批次檔
    -   任何副檔名為 `.vbs` 的 VBScript 腳本

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

-   Windows
    
    其 `$PSNativeCommandArgumentPassing` 的預設值為 `'Windows'`
    
-   non-Windows
    
    其 `$PSNativeCommandArgumentPassing` 的預設值為 `'Standard'`
    

### 相關連結

-   [PowerShell/PowerShell: PowerShell for every system!](https://github.com/PowerShell/PowerShell)
-   [Add a Windows mode for native commands that allows some commands to use legacy argument passing #15408](https://github.com/PowerShell/PowerShell/pull/15408)
