作者falcon (falken)
看板Windows
标题Re: [心得] PowerShell 那些恼人的路径 BUG
时间Sat Sep 28 07:47:00 2024
: hunandy14: 其实真实的情况是 pwsh 社群决议改掉预设万用了
: hunandy14: 他不是bug就是当初设计 不符合直觉
: hunandy14: 所以应该不会修了,那是式样不是bug
: hunandy14: 测试结果确实没有bug存在,只是恼人的设计
bug 是指对於万元字元路径的跳脱处理有误
预设万元字元我只当它是反人类设计而已
https://github.com/PowerShell/PowerShell/issues/7999
主要是讲这 bug 的影响
顺便提了两句微软的鸡婆的
设计导致多余的困扰,不是本篇的重点
由於 -like 运算子跟 cmdlet 的路径参数
对於万用字元的解读不同
这两者都是 PowerShell 原生的功能
对於同样的东西应该有相同行为
所以说,在这问题上最多只能有一方是正确的
https://i.imgur.com/YiXffzL.png
如过这是特性不是 bug 的话
那工作目录中的特殊字元应该要做独特的跳脱处理
毕竟这是在 cmdlet 内部处理的
不应该发生错误
Set-Location -LiteralPath 'D:\test`[0-2]'
Resolve-Path -Path .
Resolve-Path -LiteralPath .
使用 -Path . 与使用 -LiteralPath .
前者在任何版本都会发生错误
则只有在新的跨平台版 PS 才能得到正确路径
https://i.imgur.com/m5uoXdF.png
另外,我不是说管线那设计是 bug
bug 是指在这个工作目录把程式作为命令执行
会因为因为工作目录路径导致异常行为
https://i.imgur.com/mbG9jTo.png
Start-Process 有 -WorkingDirectory 可以用
它会直接拿你跳脱处理过的路径当 base 去组出完整路径
就能避开对於工作目录路径本身包含 ` 时的问题
而 System.Diagnostics.Process 不是 PS 的 cmdlet 所以没此问题
拿 Start-Process 举例,只因为它是 PowerShell 的 Cmdlet
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 110.28.1.89 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Windows/M.1727480822.A.7BF.html
※ 编辑: falcon (110.28.1.89 台湾), 09/28/2024 09:15:36
1F:→ hunandy14: 图1并不是因为解读不同导致的,而是路径相关的cmdlet09/28 23:00
2F:→ hunandy14: 会多做一次双引号的解释,这个估计是三张图的bug根源09/28 23:01
3F:→ hunandy14: 多做一次双引号用说的可能不好解释,放张图给你看09/28 23:03
5F:→ hunandy14: 这个逻辑虽然无误,但是就是个反人类设计09/28 23:07
6F:→ hunandy14: 图3估计是这个设计实际引发的bug...09/28 23:08
7F:→ hunandy14: 到图2图3这一步,明显是这个设计导致的bug了09/28 23:10
8F:→ hunandy14: 摁...我认为是bug了,太脑残了这09/28 23:11
9F:→ hunandy14: 图中的档案真实路径是 "D:\test\Test`[1].txt"09/28 23:13
如果逻辑一致就能说是语言特性
要 user 吞下去就算了
但 PowerShell 自己内部处理都搞不定实在太蠢了
下图是我自己实作以比对名称的方式递回搜寻路径
https://i.imgur.com/ZK32zgK.png
这样的结果才是我想要的
可以直接用管道喂路径其他 cmdlet 如 get-item
※ 编辑: falcon (110.28.1.89 台湾), 09/29/2024 00:05:02
10F:→ falcon: 网路上都找好久,都没有比较全面的方案,也只能自己动手了09/29 00:09
11F:→ falcon: 对了,如果是 te`st`[0].txt 呢?数字部分一样用万用字元 09/29 00:34
12F:→ falcon: 我试过两倍量 ` 不管用… 09/29 00:40
13F:→ falcon: 感觉要改成正规表示法才可靠了 09/29 00:42
15F:→ hunandy14: 我猜你应该快摸到他的逻辑了,就是解2次双引号 09/29 12:03
16F:→ hunandy14: 八成是为了区别那括号到底是字串还是万用字元 09/29 12:04
17F:→ hunandy14: 写一个依照万用字元表添加反引号的函式或许能解 09/29 12:06
18F:→ hunandy14: 图2应该是吃了这个亏导致的,官方cmdlet自己出bug 09/29 12:08
19F:→ smallreader: 高手过招(眼花撩乱ing 09/29 13:36
我觉得就只是最初开发者的低级失误
可能 user 用多了,改不了而已
20F:→ hunandy14: 试了一下 Resolve-Path 应该无解。改用GetFullPath吧09/29 14:07
GetFullPath() 不适用於所有 PSDrive 的路径
例如 HKLM:\
所以我才需要自己写模组
用於所有 Get-ltem 的使用场景
话说回来 万用字元
原来就只是要重复做跳脱而已
$literal = 'te`st`[1].txt'
$pattern = $literal -replace '[`\*\?\[\]]', '`$0'
if ($literal -match '[`\*\?\[\]]') {
$pattern = $pattern -replace '[`\*\?\[\]]', '`$0'
}
PowerShell 的万用字元底层应该是正规表示法
这是转换失误的BUG吧,用久了就变语言特性了
就我的理解应该要像下面这麽做
$wildcardPattern | Select-String -Pattern '`?.' -AllMatches |
ForEach-Object { $_.Matches } | ForEach-Object { $_.Value } |
ForEach-Object {
if ($_ -match '`([\[\]])') { '\' + $Matches[1] }
elseif ($_ -match '`(.)') { [regex]::escape($Matches[1]) }
elseif ($_ -match '\*') { '.*' }
elseif ($_ -match '\?') { '.' }
elseif ($_ -match '[\[\]]') { $Matches[0] }
else { [regex]::escape($_) }
} | Set-Variable regexPatternParts
$regexPattern = $regexPatternParts -join ''
而跳脱处理只需要下面这样
$wildcardPattern = $literal -replace '[`\*\?\[\]]', '`$0'
※ 编辑: falcon (110.28.1.89 台湾), 09/29/2024 15:02:46
22F:→ falcon: 这样看起来,如果以cmdlet的处理方式为准的话09/29 15:06
23F:→ falcon: -like 运算子反而才是坏的,他的行为更不规律09/29 15:06
24F:→ falcon: -like能够同时符合一般的跳脱规则,与cmdlet独特的规则09/29 15:07
25F:→ falcon: 麻烦了,不知道要按一般逻辑处理跳脱,还是照这反智规则09/29 15:16
※ 编辑: falcon (110.28.1.89 台湾), 09/29/2024 15:21:06
26F:→ falcon: 看来只能按照一般的规则,将万用字元模式转成正规表示法09/29 17:21
27F:→ falcon: 还需要一个参数来决定要不要先把输入的万元字元中的``取代09/29 17:21
28F:→ falcon: 为单个`,这样就能在两种规则中切换09/29 17:21
※ 编辑: falcon (110.28.1.89 台湾), 09/30/2024 15:27:01
29F:→ hunandy14: 今天闲着把github上的讨论串都看了 2018就有了...10/09 14:23
30F:→ hunandy14: 一个可以不用自己处理逻辑的解法是这样的10/09 14:24
32F:→ hunandy14: 不过这有效范围只有到父资料夹名称10/09 15:07
33F:→ falcon: 我目前是将万用字元路径转成正规表示法,递回方法一层用10/09 18:04
34F:→ falcon: -match运算子比对名称。结果100%可靠。但效率就不高了,递10/09 18:04
35F:→ falcon: 回方法只能停止从不正确的子目录继续往下寻找路径,但也要10/09 18:04
36F:→ falcon: 先比对过所有子目录名称,才会知道要从哪个目录往下走10/09 18:04
37F:→ falcon: 这能处理任何一层目录名称中的*或?的多重符合10/09 18:08
我有一个想法是用同名的 function 去代替 cmdlet
例如下面这样
function Get-Item {
$newArgs = $args
# 在此修改 $newargs
# 找出单独的字串或 -Path, -LiteralPath 参数的引数
# 将其作为路径扩展为根目录 ("PSDrive:\") 开头的完整的路径
Microsoft.PowerShell.Management\Get-Item @newArgs
}
但不知道 cmdlet 是如何从 $args 中区分字串与参数名称
由於引号被去掉了,取值也一样
用 .GetType() 列出类型也都一样
看起来也不是依照先後顺序与开头字元为准
https://i.imgur.com/uFynvgK.png
※ 编辑: falcon (39.9.131.112 台湾), 10/09/2024 22:09:05
※ 编辑: falcon (39.9.131.112 台湾), 10/09/2024 22:10:30
39F:→ hunandy14: 我一开始有想过转发,只是也不知道如何实现 10/09 23:04
40F:→ hunandy14: 就算参数能实现还有个大魔王管道 10/09 23:04
41F:→ hunandy14: 没找到可靠的方法估计也只能取舍了 10/09 23:05
42F:→ hunandy14: 然後就是这应该不是完全解,不然这事不会拖6年还没解 10/09 23:11