這十幾年來,在 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
來測試這個問題:
-
先透過 Chocolatey 下載安裝 UnxUtils 套件
choco install unxutils -y
-
然後執行以下命令
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
特性 ,這個特性需要特別啟用才會生效!
以下是測試該特性的步驟:
-
啟用 PSNativeCommandArgumentPassing
特性
Enable-ExperimentalFeature PSNativeCommandArgumentPassing
-
請務必重開 PowerShell 才能讓此特性生效
-
在參數列處理雙引號的部分就可以正常的解析了
echo.exe '{"Username": "will", "Password": "123"}'
輸出如下:
{"Username": "will", "Password": "123"}
注意相容性問題
啟用 PSNativeCommandArgumentPassing
特性之後,非常有可能會讓一些早期寫好的 PowerShell 在執行命令時發生錯誤,因為他會改變你 PowerShell 處理命令列參數的行為! 🔥
這個時候你肯定要認識這個 $PSNativeCommandArgumentPassing
變數,他可以讓你執行 PowerShell 的時候彈性的切換不同的命令列參數處理方式。不過 $PSNativeCommandArgumentPassing
變數講起來有點小複雜,基本上 $PSNativeCommandArgumentPassing
變數可以有三種可能的值:
-
'Legacy'
$PSNativeCommandArgumentPassing = 'Legacy'
這個設定會讓 PowerShell 回復到沒有啟用該特性的時候,確保早期的 PowerShell 程式一定可以正確執行!
-
'Standard'
這個是 Linux / macOS 作業系統下的預設值。
$PSNativeCommandArgumentPassing = 'Standard'
這個設定會讓 PowerShell 採用新的特性來解析命令列參數!
-
'Windows'
這個是 Windows 作業系統下的預設值。
$PSNativeCommandArgumentPassing = 'Windows'
設定成這個值會讓你在執行以下命令時,採用 Legacy
模式解析命令列參數,任何其他執行檔則會使用 Standard
模式來解析,以確保最大的相容性!
cmd.exe
cscript.exe
wscript.exe
- 任何副檔名為
.bat
的批次檔
- 任何副檔名為
.cmd
的批次檔
- 任何副檔名為
.vbs
的 VBScript 腳本
由於 PowerShell 新版是跨平台的命令列工具,該特性在不同的作業系統,預設值也是不同的!
相關連結