Windows 板


LINE

PowerShell 提供者是能让使用者能将各种资料存放区 (如登录档、凭证、系统环境变数)对应成类似磁碟机的结构 使用同一套命令进行新增、移除、迁移... 等管理操作 例如: New-Item、Remove-Item、Move-Item... 该虚拟磁碟机就是 PSDrive,使用 Get-PSDrive 检视卦载状态 由於 PowerShell 解析万用字元路径不可靠,必须自己实作了解析方法 在万用字元路径中,无法在档名本身含有 ` 时,同时使用万用字元进行多重符合 例如:使用 '``test?.txt' 去符合 '`test1.txt'、'`test2.txt'、'`test3.txt'... 理论上是成立的,但实际上只会得到 null 为了解决此问题就要将万用字元模式转为正规表示法去符合档名 Get-ChildItem -Name | Where-Object { $_ -match '^`test.\.txt$' } # 万用字元模式转为正规表示法 [regex]::Replace($Pattern, '`?.', { param($m) $v = $m.Value if ($v -match '^[\[\]]') { return $v } if ($v -eq '*') { return '.*' } if ($v -eq '?') { return '.' } if ($v -eq '`]') { return '\]' } return [regex]::Escape(($v -replace '`(.)', '$1')) }) 如果是一段路径,例如: 'series\season*\episode*.mp4' 就利用递回法一层层往下比对 以下说说一些主要技巧 若要先将万元字元路径展开为带根目录的万用字元路径 必须先将目前位置的路径做跳脱处理再与子路进组合为完整路径 $path = 'series\season*\episode*.mp4' $parent = $(Get-Location).Path -replace '[`\?\*\[\]]', '`$0' Join-Path $parent $path 但这边要特别注意,PowerShell 在解读万院字元路径时并不包含磁碟机代号 所以在跳脱处理时也要避开磁碟机代号 由档案系统接入而来的磁碟由於命名方式指允许 A-Z,不会有问题 但 PSDrive 允许使用包含特殊字元的字串来命名磁碟机 所以允许像 'Temp[1]:' 这样的磁碟机代号 预到这种情况需要保留磁碟机代号为原样只对其子路径做跳脱处理 $path = 'series\season*\episode*.mp4' $(Get-Location).Path -match '(^[^:]+:\?)(.*)$' $rootPath = $Matches[1] $parentWithoutRoot = $Matches[2] -replace '[`\?\*\[\]]', '`$0' Join-Path ($rootPath$parentWithoutRoot) $path 另外需要注意一些前缀特殊几解析,例如 C:[Path] -> C:\CurrentLocation[\Path] ~[\Path] -> HomeDirectory[\Path] 如果是万用字元路径,例如 '~\Path\*' 则要将特殊前缀分割出来展开为完整的目录路径 还要将此目录路径做跳脱处理再组合回去 与处理万用字元相对路径同样逻辑 # 取得磁碟机目前位置 (Get-PSDrive $PSDriveName).CurrentLocation # 取得提供者 Home 目录路径 (Get-PSProvider (Get-Location).Drive.Provider.Name).Home 还有相对路径符号的处理 目前位置: . 父目录位置: .. 1. 处理单点 (目前位置): - 位於头层: 不处理 (例如 '.\Path') - 位於非头层: 直接删除 (例如 'C:\Path1\.\Path2' -> C:\Path1\Path2') 2. 处理双点 (父目录位置): - 位於头层或前一层也是双点: 不处理 (例如 '..\..\Path') - 前一层是根目录:直接删除 (例如 'C:\..\Path' -> 'C:\Path') - 前一层是一般目录: 连同前一层一起删除 (例如 'C:\Path1\..\Path2' -> C:\Path2') 实作方式可以采用正规表示法取代 或使用纯逻辑判断处理,例如拆分成阵列,由上向下检查每层内容,使用对应处理 接下来,PowerShell 的 PSDrive 有一个很大的问题 它可接入任何提供者,并以 Windows 档案路径的形式导览 例如 'HKLM:\SOFTWARE\Microsoft' 也就是对应登陆挡机码 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft' 但如同一般档案路径,PSDrive 路径也使用 . 与 .. 代表目前位置或父目录位置 任何 . 与 .. 都会被当作相对路径符号处理掉 而在提供者的原始路径中是允与使用 . 与 .. 作为一般名称使用 这导致 PSDrive 路径,无法表示此类准确位置 如果要使用 Get-Item 取得下方机码物件,结果根本不对 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\CSPs\. PS > Get-Item HKLM:\SOFTWARE\Microsoft\Provisioning\CSPs\. | % { $_.Name } HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\CSPs PS > Get-Item HKLM:\SOFTWARE\Microsoft\Provisioning\CSPs\.. | % { $_.Name } HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning 这时候就需要使用 PSPath ,它的形式 "[模组名称\]提供者名称::提供者原始路径" Microsoft.PowerShell.Core\FileSystem::C:\Users\UserName Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft FileSystem::C:\Users\UserName Registry::HKEY_CURRENT_USER\Software\Microsoft 如果 PSPath 中的提供者原始路径是带根目录的完整路径 单双点将会被当作正常一般名称解读,而不是相对路径符号 PS > Get-Item Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\CSPs\. | % { $_.Name } HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\CSPs\. 现在我们需要一套在 PSPath 上解析万用字元的方法了 由於每种提供者原始路径的形式不一样, 对於外部模组提供者的原始路径组成方式是未知的 所以,除了提供者前缀 "[模组名称\]提供者名称::", 不能用字串处里的方式写死逻辑 只能交给 Join-Path、Split-Path 这些 cmdlet 来帮我们按照提供者方式方割、组合路径 首先需要把路径拆解成阵列,如下所示 'FileSystem::C:\Users\UserName' -> 'FileSystem::C:\', 'Users', 'UserName' 'FileSystem::C:Users\UserName' -> 'FileSystem::C:', 'Users', 'UserName' 'FileSystem::/mnt/c/Users/UserName' -> 'FileSystem::/', 'mnt', 'C', 'Users', 'UserName' 'Registry::HKEY_CURRENT_USER\Software' -> 'Registry::', 'HKEY_CURRENT_USER', 'Software' # 用 Split-Path 由後往前分割出最後一项存入阵列 $splitPath = @($leaf) while (-not [string]::IsNullOrWhiteSpace($path)) { Split-Path $path -Parent $leaf = Split-Path $path -Leaf $path = Split-Path $path -Parent $splitPath += @($leaf) } $newSplitPath = $splitPath[($splitPath.Count - 1)..0] 这里会与到许多问题 问题1: Split-Path -Leaf 分割到最後一项,得到错误提供者路径头项 PS > Split-Path -Leaf 'FileSystem::/' PS > Split-Path -Leaf 'FileSystem::C:' C:\ PS > Split-Path -Leaf 'FileSystem::~' UserName 解决方法是在最後方割前跳出回圈(如下所示),头项另行处理 'FileSystem::C:\Users\UserName' -> 'FileSystem::C:\', 'Users', 'UserName' 'FileSystem::C:Users\UserName' -> 'FileSystem::C:Users', 'UserName' 'FileSystem::/mnt/c/Users/UserName' -> 'FileSystem::/mnt', 'C', 'Users', 'UserName' 'Registry::HKEY_CURRENT_USER\Software' -> 'Registry::HKEY_CURRENT_USER', 'Software' $splitPath = @($leaf) while (-not [string]::IsNullOrWhiteSpace($path)) { $parent = Split-Path $path -Parent if ([string]::IsNullOrWhiteSpace($parent)) { $splitPath += @($path); break } $leaf = Split-Path $path -Leaf $path = $parent $splitPath += @($leaf) } $splitPath = $splitPath[($splitPath.Count - 1)..0] # 由於提供者前缀是已知形式,所以对 $splitPath[0] 的分割 $splitHeadPath = $splitPath[0] -split '(?<=::)',2 # 然後再用 Test-Path 验证是否可用,以免过度分割 if (Test-Path $splitHeadPath[0] -IsValid) { # 把 $splitPath 的头项移除,在前方插入新的分割结果 } 问题2:Split-Path -Leaf 擅解析相对路径符号,导致循环分割结果错乱 PS > Split-Path 'FileSystem::~\Desktop\.' -Parent FileSystem::~\Desktop PS > Split-Path 'FileSystem::~\Desktop\.' -Leaf Desktop PS > Split-Path 'FileSystem::~\Desktop\..' -Parent FileSystem::~\Desktop PS > Split-Path 'FileSystem::~\Desktop\..' -Leaf UserName # 解决方法是先用 Join-Path 验证最後一样是不是 . 或点 .. $parent = Split-Path $path -Parent if ($path -eq (Join-Path $parent '.') -or # 带尾分隔符号,以 FileSystem 为例: 'FileSystem::path\.\' $path -eq (Join-Path $parent '.' | Join-Path -ChildPath $null)) { $leaf = '.' } 则以同样方式测试尾项是不是 .. 如果都是 false 则 $leaf = Split-Path $path -Leaf 把这麽方法封装新函式 (例如 Split-PathFixed) 代替 Split-Path 即可 问题3:Join-Path 也有奇怪 Bug PS > Join-Path 'FileSystem::~' '.\file' C:\FileSystem::~\.\file 解决方法是 如果输入是简短提供者名称,则先将其扩充为完整 例如:'Microsoft.PowerShell.Core\FileSystem::~' PS > Join-Path -Path "FileSystem::C:" -ChildPath "file" FileSystem::C:\file PS > Join-Path -Path "C:" -ChildPath "file" C:\file 多出不该有的分隔线,原本的 C 曹目前目录变成根目录 这个就只能为他们单独写一项组合方式了 # 取得提供者所属模组名称 (Get-PSProvider $providerName).ModuleName PS > Join-Path 'Microsoft.PowerShell.Core\FileSystem::~' '.\file' Microsoft.PowerShell.Core\FileSystem::~\.\file 组合路径後,如果原本是短名称,再将前方的模组名称移除就好 把这麽方法封装新函式 (例如 Join-PathFixed) 代替 Join-Path 即可 到现在,可以将路径按照期望确实最小分割 进入到处理相对路径符号 . 与 .. 的部分了 首先要先厘清原始提供者路径中是否保留 . 与 .. 作为相对路径符号 测试前要先弄出一个含有不存在中间层与 . 还有 .. 的路径用来测试 把此路径喂给 Get-Item 看看会有什麽结果 因为 Get-Item 会将根目录开头的 PSPath 中的 . 与 .. 都视为一般名称 $provider = Get-PSProvider $Name $psDrive = Get-PSdrive | Where-Object { $_.Provider.Name -eq $provider.Name } | Select-Object -First 1 $providerRootPSPath = "$($provider.Name)::$($psDrive.Root)" if (-not [string]::IsNullOrWhiteSpace($provider.ModuleName)) { $providerRootPSPath = "$($provider.ModuleName)\$providerRootPSPath" } # 以 FileSystem 为例就是 'Microsoft.PowerShell.Core\FileSystem::C:\foo\.\..' $testPSPath = Join-PathFixed $providerRootPSPath 'foo' | Join-PathFixed -ChildPath '.' | Join-PathFixed -ChildPath '..' $item = Get-Item -LiteralPath $testPSPath -ErrorAction SilentlyContinue if ($? -and $item.PSPath -ne $testPSPath) { # 项目存在,但返回了不同的路径(例如退回根目录) # 代表此提供者保留了纯点作为相对路径指标 return $true } else { # 执行失败(找不到该特殊名称)或返回了相同路径 # 代表提供者允许纯点作为子项目名称 return $false } 为了节省资源,可以把已知的提供供者支援情形列为标单供查询 只对未知的提供者进行测试 # 微软官方核心提供者对於相对路径符号 (".", "..") 的支援状态 $script:ProviderSupportsRelativePathTokens = @{ 'Microsoft.PowerShell.Core\FileSystem' = $true # 档案系统 'Microsoft.PowerShell.Core\Registry' = $false # 登录档 'Microsoft.PowerShell.Core\Alias' = $false # 别名 'Microsoft.PowerShell.Core\Environment' = $false # 环境变数 'Microsoft.PowerShell.Core\Function' = $false # 函式 'Microsoft.PowerShell.Core\Variable' = $false # 变数 'Microsoft.PowerShell.Core\Certificate' = $true # 凭证 'Microsoft.WSMan.Management\WSMan' = $false # WSMan } 处理完路径中的相对路径符号 接着就能把拆分、清理後的万用字元提供者路径丢去递回比对路径每层名称了 将其流程写成cmdlet并命名为 Resolve-WildcardPath 以下是示范结果 PS > Resolve-WildcardPath 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\*\.' Registry::\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\CSPs\. 注册表的 . 与 .. 会被正确当为一般名称,但 FileSystem 会被解析掉 PS > Resolve-WildcardPath 'FileSystem::C:\Users\UserName\Desktop\*\*\..\.\..\test\.\a```[*`]\a```[[3-5]`].txt' FileSystem::C:\Users\UserName\Desktop\test\a`[1-3]\a`[3].txt FileSystem::C:\Users\UserName\Desktop\test\a`[4-6]\a`[4].txt FileSystem::C:\Users\UserName\Desktop\test\a`[4-6]\a`[5].txt 在 PS 路径模式工作目录中,使用提供者相对路径 PS > Set-Location -LiteralPath 'FileSystem::C:\Users\UserName\Desktop\test' PS > Resolve-WildcardPath 'a```[*`]\a```[[3-5]`].txt' Microsoft.PowerShell.Core\FileSystem::C:\Users\UserName\Desktop\test\a`[1-3]\a`[3].txt Microsoft.PowerShell.Core\FileSystem::C:\Users\UserName\Desktop\test\a`[4-6]\a`[4].txt Microsoft.PowerShell.Core\FileSystem::C:\Users\UserName\Desktop\test\a`[4-6]\a`[5].txt 不过由於这套流程预设输入的 PSPath 带的提供者内部路径是从根目录开始的 所以无法展开目录前缀符号 Resolve-WildcardPath 'FileSystem::~\Desktop\*\*\..\.\..\test\.\a```[*`]\a```[[3-5]`].txt' FileSystem::~\Desktop\test\a`[1-3]\a`[3].txt FileSystem::~\Desktop\test\a`[4-6]\a`[4].txt FileSystem::~\Desktop\test\a`[4-6]\a`[5].txt 不过做到这地步,cmdlet 的 -Path 也能正常找到目标了 也就没什麽动力解决这问题了 PS > Get-Item -LiteralPath 'FileSystem::~\Desktop\test\a`[1-3]\a`[3].txt' Directory: C:\Users\UserName\Desktop\test\a`[1-3] Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 2026/6/16 20:39 22 a`[3].txt 但 PSPath 也是有大问题 'FileSystem::C:Desktop' -> 被 cmdlet 视为 C 曹目前位置下的 Desktop,这符合定义 'FileSystem::C:' -> 被 cmdlet 视为 "C:\",这理反而与定义不符了 'FileSystem::C:*' -> 理论上是符合 C 曹目前位置下的所以项目,但 cmdlet 完元不认得 完全不知道怎麽解决 因为在处理 PSPath 是以不知道提供者原始路径形式下 不写死的字串处理,只能使用 PowerShell 的路径处理功能来实现 毕竟 PSPath 是用来应对非 FileSystem 的提供者无法完美卦载到 PSDrive 所以只能到此为止了 PowerShell 的坑实在是太多 Windows 两个内建命令壳层 CMD 老旧又难用 PowerShell 强大,但到处都是暗坑等你踩 写好的完整模组我就不献丑了 如果有人需要在贴出来 --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 39.15.49.188 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Windows/M.1782227729.A.3E2.html ※ 编辑: falcon (39.15.49.188 台湾), 06/23/2026 23:33:51
1F:→ kyrc: 想请问这是使用 5.1 还是 PowerShell 7.x 版的踩坑纪录? 06/23 23:27
2F:→ falcon: 最新社群版也只是解决工作目录含有`时,解析相对路径错误 06/23 23:37
3F:→ falcon: 还是无法处理稍微复杂的万用字元路径 06/23 23:38
4F:→ labbat: 改呼叫python3脚本,这样能不能比较不辛苦 06/24 11:26
PowerSehll 毕竟是 Windows 内建 其意义就是拿来即用不需额外安装 有研究解决问题的价值在 如果只是要达到特定目的 那用什麽都没差 ※ 编辑: falcon (39.15.48.150 台湾), 06/25/2026 21:42:15







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:e-shopping站内搜寻

TOP