[心得] PowerShell 那些惱人的路徑 BUG

看板Windows作者 (falken)時間3月前 (2024/09/18 23:54), 2月前編輯推噓5(5010)
留言15則, 5人參與, 2月前最新討論串1/3 (看更多)
首先,目錄結構如下 - D:\test`[0-2]\test`[0].txt - D:\test`[0-2]\test`[1].txt - D:\test`[0-2]\test`[2].txt PowerShell 的 Cmdlet 預設使用 -Path 接受萬用字元模式路徑, 但 Cmdlet 無法完全正確解讀萬用字元模式路徑中的跳脫處理。 例如,一旦利用跳脫處理來符合目錄或檔案名稱中的特殊字元, 若也使用 *, ?, [ ] 要符合多種組合, 即使字串模式無誤,也找不到任何項目,如下方範例所示。 Get-Item 'D:\test```[0-2`]\test```[[0-2]`].txt' Get-Item 'D:\test```[0-2`]\test```[*`].txt' Get-Item 'D:\test```[0-2`]\test```[?`].txt' 以上三個 Get-Item 命令回傳都是 null 若要避免此問題,由於路徑中其他位置的名稱不受此問題影響, 可以先用 Drive:\path\to\dir\* 符合所有子項目的路徑, 再用沒這問題的 -like 運算子篩選子項目名稱。 $items = Get-Item -Path 'D:\test```[0-2`]\*' $items | Where-Object {$_.Name -like 'test```[[0-2]`].txt'} $items | Where-Object {$_.Name -like 'test```[*`].txt'} $items | Where-Object {$_.Name -like 'test```[?`].txt'} 工作目錄路徑本身帶有反引號字元與其他特殊字元時, PowerShell 無法正確解讀相對路徑。無論是使用 -LiteralPath 指定路徑; 或是使用 -Path 指定萬用模式路徑,即使做了正確的跳脫處理。 Set-Location -LiteralPath 'D:\test`[0-2]' Get-Item -Path '*' 0 .. 2 | ForEach-Object { 'test`[' + $_ + '].txt'} | Where-Object { Test-Path -LiteralPath $_} | Get-Item -LiteralPath {$_} 以上兩個 Get-Item 命令回傳都是 null 若要繞過此問題,可以使用完整路徑,如下方範例所示。 即根目錄開頭 Drive:\ 的路徑, 例如 C:\Users\UserName\Desktop\MyFile 這樣不行 C:Desktop\MyFile (新版的 PowerShell 已經修復了 -LiteralPath 使用相對路徑時問題) Get-Item -Path 'D:\test```[0-2`]\*' 0 .. 2 | ForEach-Object { 'D:\test`[0-2]\test`[' + $_ + '].txt'} | Where-Object {Test-Path -LiteralPath $_} | Get-Item -LiteralPath {$_} 除了相對路徑,磁碟機代號也受此問題影響。 (新版的 PowerShell 已經修復了,使用磁碟機代號時,不會遇到此問題) Set-Location -LiteralPath 'D:\test`[0-2]' Test-Path -LiteralPath 'D:' 以上 Test-Path 命令回傳 False,明顯是錯誤 另外,若在此目錄執行外部程式,例如 cmd /c "echo %CD% & pause", 則將意外在新視窗執行,並將 D:\ 視為工作目錄。 若要避免此問題,其中一個解決分法如下,改用 Start-Process 執行程式, 並使用 -WorkingDirectory 設定工作目錄。 Set-Location -LiteralPath 'D:\test`[0-2]' Start-Process ` -FilePath 'cmd' ` -ArgumentList '/c "echo %CD% & pause"' ` -WorkingDirectory ($PWD.Path -replace '[`\[\]]', '`$0') ` -Wait ` -NoNewWindow 執行結果如下,正常了 D:\test`[0-2] Press any key to continue . . . 總結 對於 PowerShell 的 Cmdlet 若要對應任何萬元字元模式路徑, 也只能遞迴方法一層一層使用 -like 去比對所有子項目的名稱 對於外部程式,使用 Start-Process 就代表不能用 | 符號來通過管線傳遞資料 不過 Windwos PowerShell 5.1 的管線也暗藏陷阱 PowerShell 把所有經由管線傳遞的資料都當作字串解碼 若要使用影音程式,還是需要 Start-Process 來呼叫 CMD 使用 CMD 的管線功能來避開此問題 PowerShell 雖然功能強大,但一堆反人類設計與 BUG Windows 的命令殼層也就 CMD 與 PowerSell 用 PowerShell 還是比較容易實現較複雜的操作 雖然反直覺的地方可能改不了 但還是希望微軟好好加把勁修 BUG -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 39.12.65.212 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/Windows/M.1726674885.A.AA7.html

09/19 02:46, 3月前 , 1F
直接用c語言寫,或者改用python script腳本
09/19 02:46, 1F

09/19 03:32, 3月前 , 2F
`````````這麼多顆到底要怎麼看 哦對吼\被拿去當路
09/19 03:32, 2F

09/19 03:32, 3月前 , 3F
徑分隔符了 歷史共業XD
09/19 03:32, 3F
從markdown筆記上直接複製過來的 刪除了多餘語法,改用ptt上色,應該好一些

09/19 13:21, 3月前 , 4F
用 PS 還得繞過這些 bug 真的是活受罪,我最後都乾脆
09/19 13:21, 4F

09/19 13:21, 3月前 , 5F
改用 WSL 或 MSYS2 了
09/19 13:21, 5F
BUG就看MS什麼時後要修 至於反人類設計,為了相容性只能是歷史共業了 MS 的產品還是那個熟悉的味道… ※ 編輯: falcon (27.51.74.66 臺灣), 09/19/2024 17:54:53

09/20 04:04, 3月前 , 6F
高手 踩地雷大全
09/20 04:04, 6F

09/27 12:11, 2月前 , 7F
其實真實的情況是 pwsh 社群決議改掉預設萬用了
09/27 12:11, 7F

09/27 12:12, 2月前 , 8F
他不是bug就是當初設計 不符合直覺
09/27 12:12, 8F

09/27 12:12, 2月前 , 9F
所以應該不會修了,那是式樣不是bug
09/27 12:12, 9F

09/27 13:02, 2月前 , 10F
測試結果確實沒有bug存在,只是惱人的設計
09/27 13:02, 10F

09/27 13:05, 2月前 , 11F
細節可以參考這篇 https://bit.ly/3Y2Tn4s
09/27 13:05, 11F

09/27 13:09, 2月前 , 12F
第二個問題管道傳送是字符而不是二進制
09/27 13:09, 12F

09/27 13:09, 2月前 , 13F
這個就也是為了方便性犧牲掉的設計
09/27 13:09, 13F

09/27 13:11, 2月前 , 14F
這邊推另一個 C# 的解 https://bit.ly/4dmIXAQ
09/27 13:11, 14F

09/27 13:15, 2月前 , 15F
趁早換了pwsh至少有社群幫你扛著改掉這些白癡設計
09/27 13:15, 15F
※ 編輯: falcon (110.28.1.89 臺灣), 09/28/2024 07:42:36
文章代碼(AID): #1cwlV5gd (Windows)
文章代碼(AID): #1cwlV5gd (Windows)