PowerShell 通配符 Get-Item 無法帶有方括號名稱的檔案
這算是 PowerShell 5.1 預設給的方便的,不過在不知情的情況下就造成bug了
假設有一個 [1]File.txt
的檔案,於是就這樣獲取
Get-Item [1]File.txt
居然什麼都沒有,於是乎聰明如你,用引號總能解吧?
Get-Item "[1]File.txt"
Get-Item '[1]File.txt'
想不到吧很遺憾還是沒有辦法
這東西叫做通配符,內建在 Get-Item 裡的所才沒法通過引號處理的
about Wildcards - PowerShell | Microsoft Learn
情況1
如果你只是單純想解決括號問題,沒有要搞通配符號組合,參考這個就能解了。
Get-Item -LiteralPath 'Test[1]'.txt
Get-Item '.\Test`[1`].txt'
Get-Item ".\Test```[1```].txt"
情況2
另一個坑是當括號與星號一起出現時,又會導致另一個解析問題,括號被當作通佩服一起解釋了,就是你想匹配 Test[*].txt 多個檔案的時候。
這時候情況更複雜了一些,你得連反引號都一起傳進去才能,免得反引號在傳入的當下被解析了少了一次解析。
Get-Item '.\Test``[*``].txt'
Get-Item ".\Test`````[*`````].txt"
測試結果
情況3
2024-09-28 發現一個新問題 'Test``[*``].txt' 實際上匹配的並不是 Test[*].txt 而是 Test*.txt,說起來繞口,原因是那個方括號被當作萬用字解讀了。
如何更明白的理解問題可以看下面這個例子
Test[1].txt 的檔案
Get-Item '.\Test``[[0-1]``].txt'
這樣寫出來應該就很好理解了,因為萬用字的匹配是在 cmdlet 裡面做的,所以才導致這個反人類思維的解...
第一層反引號會在傳入的時候被解掉,此時算法實際吃到的是一個反引號的 `[ 所以她成功識別是一個引號,第二個由於就是引號沒什麼好說的吃到引號當作萬用字解讀。
也就是可以理解成 [0-1] 這東西會被當作一個特殊字串對待,剩下的保持原本模樣。
那為什麼 '.\Test`[1`].txt' 能夠被正確識別呢? 因為 [1] 沒有構成合法的萬用字元所以被當作字串解讀了 (這鬼邏輯...雖然是正確的但別這樣設計搞人啊)
情況4
以為這樣就結束了嗎? 不還有一個更鬼畜,記得先溫得好情況3的神奇邏輯,這是同一套邏輯的變態版,現在考慮到檔名含有雙引號的情況
Test`[1].txt 的檔案
Get-Item '.\Test``````[[0-1]``].txt'
居然是要添加4個引號? 是的這個邏輯的是正確,因為傳入的時候會被吃掉一次只剩2個,真正在運算的時候又被吃掉一次只剩1個,所以正確的識別了
這個鬼邏輯來自於,不知道為啥路徑相關的cmdlet會對路徑做一次多餘的雙引號解析導致的,啥原因設計成這樣就不知道了。
下面是測試的方法,看了應該就秒懂上面的 情況4 發生什麼事情了
(注意雙引號與單引號)
'.\Test`[1].txt' -like ".\Test``````[[0-1]``].txt"
對他們是相等的...也就是說對於所有路徑相關的 Get-Item, Test-Path 等等必須自己有自覺的認識到,即使我用了單引號避免被解釋,但實務上進去還會被解析一次。
這個真該打屁股了誰寫的反人類設計...
不過嘴砲歸嘴砲,八成是身處歷史當下的那群人,遇到某個無法解決的問題,提出的無可奈何的解吧...。現在好拉已經過這麼久了,想修估計也改不了了,成為萬世毒瘤了
對於這個局面的處置方法
對於這個局面要人性化的處置可以參考這下面這個解法
Test-Path ('Test```[[0-1]`].txt' -replace('`','``'))
Test-Path ('Test```[*`].txt' -replace('`','``'))
這少這樣理解度就高一點了,為什麼會有三個是因為需要反轉意一個反引號跟方括號導致的,是可以讀懂的代碼。
參考
- Wildcard matching when filename has square brackets · PowerShell/PowerShell · Discussion #18146 (github.com)