PowerShell 系列写到第七篇终于要聊一聊这个系列里最“Windows 味”的概念——提供程序Provider和驱动器PSDrive。说实话很多人用了很久 PowerShell天天敲cd C:\、dir却不知道这两个术语是什么。哪怕你只是想在终端里少打几个字或者想把注册表、证书库、环境变量当文件夹一样逛这两个概念都是绕不开的。提供程序就是把 Windows 里乱七八糟的数据存储统一抽象成“路径 目录 文件”的操作模型。驱动器则是你进入这些数据世界的挂载点像是给不同数据存储分配的“盘符”。明白了这一层你才能真正体会到为什么 PowerShell 叫“Shell”——它不再只是命令解释器而是一套能把你所有常用管理界面注册表编辑器、证书管理、环境变量面板全部命令行化的框架。这篇内容会带你把提供程序和驱动器的常见玩法过一遍包括系统默认挂了哪些、怎么自定义挂载、怎么用它们做证书巡检和开机自启项备份最后会把那些容易踩的坑整理出来比如网络驱动器断连、找不到驱动器、中文乱码以及一个连接 SQL Server 时非常常见的 SSL 证书报错处理。适合正在系统学习 PowerShell 的运维、开发也适合把 PowerShell 当瑞士军刀日常使用的普通玩家。1. 提供程序PowerShell 访问一切数据的统一入口1.1 为什么会有“提供程序”这种抽象Windows 里的数据存储形态千差万别。文件系统是一棵带盘符的目录树注册表是一棵双根的键值树证书库是一个按逻辑分区组织的对象列表环境变量是散列的键值对别名、函数、变量又各有各的存放方式。如果每种数据都设计一套专属命令PowerShell 就会变成一锅粥。提供程序解决的就是这个问题它把不同数据存储包装成一个统一的接口让你的操作习惯只需要学一次。无论底层是文件还是注册表你都可以用Get-ChildItem列内容、用Set-Item改值、用Remove-Item删除。我更喜欢把 Provider 理解成“适配器”。它识别底层数据存储的真实结构然后把这些结构映射成 PowerShell 的路径规则。比如说注册表里“项”对应路径中的容器“值”对应路径中的叶子。你在HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run下看到一个个子项本质上就是在用文件浏览的方式看注册表结构。1.2 系统自带的 8 个提供程序PowerShell 一启动就会挂载一批内置提供程序。想确认当前会话有哪些直接跑一条命令Get-PSProvider输出会包含 Name、Drives、Capabilities 三列。Name 是提供程序的名字Drives 是这个提供程序当前挂载的驱动器Capabilities 则是它支持的能力集合比如是否支持过滤、是否支持凭据、是否支持事务。常见内置提供程序可以整理成下面这张表提供程序名称默认驱动器背后数据存储典型操作AliasAlias:PowerShell 别名表查看、创建别名EnvironmentEnv:Windows 环境变量读取、临时修改环境变量FileSystemC:, D: 等文件系统文件目录操作FunctionFunction:PowerShell 函数定义查看、定义函数RegistryHKLM:, HKCU:Windows 注册表查看、增删改注册表项和值VariableVariable:PowerShell 变量查看、修改变量CertificateCert:证书存储区查看、导入、删除证书WSManWSMan:WS-Management 配置管理 WinRM 相关配置注意 Registry 提供了两个默认驱动器HKLM: 指向 HKEY_LOCAL_MACHINEHKCU: 指向 HKEY_CURRENT_USER。这样做是故意的因为注册表本身就分为机器级和用户级拆成两个驱动器路径语义更清楚权限边界也更明确。1.3 第三方提供程序的生态提供程序不只是一群内置适配器它本身是可扩展框架。只要用 .NET 写一个继承 Provider 基类的类就能把任意数据源暴露成 PowerShell 路径。这个生态里最有代表性的就是微软自己的 SqlServer 模块。装好 SqlServer 模块后你会多一个 SQLSERVER: 驱动器Import-Module SqlServer cd SQLSERVER:\SQL\localhost\DEFAULT\Databases Get-ChildItemSQL Server 的实例、数据库、表对象被映射成目录你可以像逛文件系统一样逛数据库。第三方厂商如果不想让用户去记一堆专属 cmdlet完全可以靠提供程序把 API 目录化。这也是很多人低估的一块以为提供程序只是“命令行里的资源管理器”实际上它是一个数据访问生态的门面。2. PSDrive不只是盘符是“挂载点”2.1 查看当前驱动器的全家福提供程序定义的是数据访问规则真正让你进入这些数据的入口叫 PSDrive。每个驱动器都指向提供程序管辖范围内的某个根路径。用Get-PSDrive可以看到当前会话里所有驱动器Get-PSDrive典型输出长这样Name Used (GB) Free (GB) Provider Root ---- --------- --------- -------- ---- Alias Alias C 100 50 FileSystem C:\ Cert Certificate \ D FileSystem D:\ Env Environment Function Function HKCU Registry HKEY_CURRENT_USER HKLM Registry HKEY_LOCAL_MACHINE Variable VariableRoot 这一列很有讲究。文件系统驱动器的 Root 是C:\注册表驱动器的 Root 是HKEY_CURRENT_USERCert 驱动器的 Root 则是\表示整个证书提供程序的项。你还可以只查看某个驱动器比如Get-PSDrive C。搞懂 Root 的意义你就明白为什么Cert:\后面还能继续接CurrentUser\My。因为 Certificate 这个提供程序把自己的数据根定位成一个虚拟根路径下面再按 CurrentUser、LocalMachine 区分存储区。2.2 New-PSDrive 创建自己的驱动器日常最实用的功能之一就是这个把常用路径挂成你自己的盘符。先看最常见的网络共享映射。比如你经常要访问\\nas\backup这个共享目录但实在不想每次都打一长串 UNC 路径就可以这样New-PSDrive -Name Data -PSProvider FileSystem -Root \\nas\backup -Persist cd Data:这里有几个关键参数。-Name指定新驱动器名称不能跟现有驱动器重名。-PSProvider必须写清是想用哪个提供程序创建FileSystem 对应文件系统Registry 对应注册表Certificate 对应证书库。-Root则是这个驱动器映射的源根路径。-Persist是很特殊的一个参数它的作用是让驱动器在 PowerShell 会话结束以后依然存在。实现方式说白了就是帮你创建一个映射网络驱动器所以只有 FileSystem 提供程序支持它是合理的注册表、证书这类数据源天然不存在“跨会话持久化”的概念。映射注册表路径也一样顺手。比如某个软件把配置放在深层注册表项里你不想反复cd HKEY_LOCAL_MACHINE\SOFTWARE\...就可以这样New-PSDrive -Name AppCfg -PSProvider Registry -Root HKLM:\SOFTWARE\MyApp\Config Set-Location AppCfg: Get-ChildItem证书库同样可以挂载New-PSDrive -Name MyCert -PSProvider Certificate -Root Cert:\CurrentUser\My cd MyCert:个人经验是驱动器名不要带中文、不要带空格能省掉后续路径引用时的一堆转义问题。命令行里Set-Location到自定义驱动器时千万别忘记驱动器名后面带冒号比如AppCfg:少了冒号 PowerShell 会把AppCfg当成目录名去解析。2.3 Remove-PSDrive 与 -Persist 的行为差异删除一个驱动器用Remove-PSDriveRemove-PSDrive -Name AppCfg这里最容易踩坑的地方是不能删除当前正处于其中的驱动器。假如你现在停在AppCfg:\下面直接执行Remove-PSDrive AppCfg系统会报错说驱动器正在使用中。必须先切走cd C:\ Remove-PSDrive -Name AppCfg对于带-Persist创建的驱动器Remove-PSDrive会同时把资源管理器里的映射网络驱动器一并删掉。所以如果你只是想让 PowerShell 里暂时不见它而资源管理器里网络盘还要保留不要用 Remove-PSDrive应该直接用Set-Location走到别处就好PowerShell 驱动器对象依然存在。还有一种常见需求是更新已有的驱动器。如果Data:已经被挂载过再次New-PSDrive -Name Data会报错这时候可以加-Force强制重建New-PSDrive -Name Data -PSProvider FileSystem -Root \\nas\newpath -Force2.4 驱动器的作用范围与生命周期New-PSDrive还有-Scope参数用来控制驱动器的可见范围可选值跟变量作用域一样Global、Local、Script。如果不指定默认创建的驱动器在当前会话中一直存在脚本结束也不消失。但如果你在脚本里用了-Scope Script脚本跑完驱动器就被回收。比如写一个临时巡检脚本里面挂载一个临时驱动器做操作结束时不想留下任何痕迹用-Scope Script就很干净。另外提一个容易被混淆的点环境变量驱动器 Env: 和变量驱动器 Variable: 是两个完全不同的东西。Env: 里面装的是 Windows 环境变量改它影响程序启动时的环境Variable: 里面装的是 PowerShell 会话变量改它只影响当前会话。一个例子就能区分Get-ChildItem Env:看到的是 PATH、USERPROFILE 这些Get-ChildItem Variable:看到的是$PSHOME、$PROFILE这类 PowerShell 内部变量。3. 把“目录思维”应用到非文件类数据3.1 用目录操作注册表提供程序带来的最大思维转变就是你可以不再依赖 regedit 图形界面。看开机自启项过去要 winR 敲 regedit 然后一层层点进去现在直接在 PowerShell 里切到注册表驱动器cd HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run Get-ChildItem这条命令会把 Run 键下的所有启动项列出来。注意每个启动项显示的是注册表“值”而不是子键Get-ChildItem会把它们当成叶子对象展示。新增注册表项也不像想象中麻烦。创建一个新键用New-Item创建一个值用New-ItemPropertyNew-Item -Path HKCU:\Software\MyAppTool -Force New-ItemProperty -Path HKCU:\Software\MyAppTool -Name Version -Value 1.0.0 -PropertyType String因为 HKCU 是当前用户配置不需要管理员权限适合用来做用户级软件配置管理。如果是 HKLM 下面通常需要管理员身份运行 PowerShell否则会报没有权限访问注册表项的错。3.2 用目录操作证书库证书库本来是最难用命令行操作的东西之一曾几何时只能在 mmc 里加证书管理单元。用提供程序访问证书存储区简单到让人觉得怀疑Get-ChildItem Cert:\CurrentUser\My这会列出当前用户个人证书存储区里的所有证书。每个证书对象都有很多属性比如 Subject、Thumbprint、NotAfter、NotBefore。你想看哪些证书快过期一条管道命令就够了Get-ChildItem Cert:\CurrentUser\My | Select-Object Subject, NotAfter, Thumbprint再配合Where-Object过滤出 30 天内过期的证书这就是一个非常实用的巡检脚本。剖面看证书路径结构是Cert:\CurrentUser\My、Cert:\LocalMachine\My这样分区的LocalMachine 目录需要管理员权限才能访问这个限制跟文件系统里的C:\Program Files的 ACL 逻辑是一样的。3.3 环境变量、变量、函数、别名都能逛环境变量也可以用路径方式访问Get-ChildItem Env:抓单个环境变量直接用$env:变量名但如果你想批量处理比如把当前所有环境变量导出成表格就可以Get-ChildItem Env: | Export-Csv ...。写环境变量要注意在 PowerShell 里直接赋值只对当前进程有效。想让用户级环境变量持久化要这样做[Environment]::SetEnvironmentVariable(MY_VAR, value, User)再说 Variable、Function、Alias 这仨。Variable: 里能直接看到当前会话所有变量虽然平时不太会去cd Variable:操作但偶尔排查$PROFILE之类的变量来自哪里时直接Get-ChildItem Variable: | Where-Object Name -like *Profile*比瞎猜快得多。Function: 更实用你可以快速查看某个函数的定义比如Get-Content Function:\prompt能直接看到默认的 prompt 函数源码。Alias: 同理想查所有指向Get-ChildItem的别名一条命令就能搞定。4. 三个可以直接抄的实战脚本4.1 证书到期巡检证书到期是很多系统故障的隐形炸弹。在提供程序出来以前你得打开证书管理单元挨个看每张证书的到期时间。现在脚本化非常简单$expiring Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.NotAfter -lt (Get-Date).AddDays(30) -and $_.NotAfter -gt (Get-Date) } if ($expiring) { $expiring | Select-Object Subject, NotAfter, Thumbprint | Format-Table } else { Write-Host 没有即将过期的证书 }注意我加了一个-and $_.NotAfter -gt (Get-Date)条件这是为了过滤掉已经过期的历史证书只看“未来 30 天内会过期”的。实际巡检时你还会想区分是本地机器证书还是当前用户证书那就把路径换成Cert:\LocalMachine\My并记得用管理员身份运行。这个脚本可以作为每天的计划任务把输出重定向到日志文件。4.2 开机自启项备份排查开机自启是运维的日常。注册表里 Run 键是最常见的自启集中地通过提供程序可以直接把自启项导出成 CSV$items Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run $items.PSObject.Properties | Where-Object { $_.Name -notmatch ^PS } | Select-Object Name, Value | Export-Csv -Path $env:USERPROFILE\Desktop\RunItems.csv -NoTypeInformation这条命令里有两个细节值得说明。$items.PSObject.Properties会返回注册表项下的所有属性包括 PowerShell 自动附加的内置属性比如 PSPath、PSParentPath 等所以要用-notmatch ^PS把它们过滤掉。$env:USERPROFILE则是环境变量提供程序在起作用不需要手写用户目录路径。如果你想把这个脚本放到开机自启里执行注意两件事第一脚本文件要用 UTF-8 with BOM 保存否则 Windows PowerShell 5.1 跑带中文注释的脚本很容易乱码或解析错误第二用任务计划程序触发比塞到 Run 键里更可靠触发时加上 -WindowStyle Hidden 能避免黑色窗口弹出来吓人一跳。4.3 PowerShell 连接 SQL Server 时的 SSL 证书链报错处理有朋友在 PowerShell 里连接 SQL Server 时报错内容差不多是这样[08001] [Microsoft][ODBC Driver 17 for SQL Server]SSL 提供程序: 证书链是由不受信任的颁发机构颁发的。 (-2146893019) [08001] [Microsoft][ODBC Driver 18 for SQL Server]SQL Server 客户端无法建立连接这个错误不是 PowerShell 本身的问题是 SQL Server 默认使用自签名证书加密连接而新版 ODBC 驱动对证书链的校验越来越严格。以前的旧驱动可能假装没看见自签名证书现在直接拒绝建立连接。解决办法有几种。如果只是开发测试环境可以在连接字符串里关掉服务器证书校验$connString Serverdb01;Databasemaster;User Idapp;Password你的密码;TrustServerCertificateTrue $conn New-Object System.Data.SqlClient.SqlConnection $connString $conn.Open()注意 SqlClient 的连接字符串要用True但如果是 ODBC 驱动写法稍有区别$odbcConn New-Object System.Data.Odbc.OdbcConnection $odbcConn.ConnectionString Driver{ODBC Driver 17 for SQL Server};Serverdb01;Databasemaster;Uidapp;Pwd你的密码;TrustServerCertificateyes; $odbcConn.Open()如果用的是sqlcmd则有个-C参数可以临时信任自签名证书sqlcmd -S db01 -U app -P 你的密码 -C -Q SELECT VERSION不过我要多提醒一句TrustServerCertificate只是绕过了证书校验传输仍然是加密的但身份验证安全等级会降低。生产环境不要图省事正确做法是在 SQL Server 实例上安装受信任 CA 签发的证书然后配置成强制加密客户端这边反而更简单。5. 常见问题与避坑指南5.1 找不到驱动器或 Set-Location 无法识别这是提供程序相关报错里最常见的两类。第一类是真的找不到驱动器。报错类似Set-Location : Cannot find drive because a drive named Data does not exist.。原因无非是驱动器没创建、你换了一个 PowerShell 会话、-Scope Script的临时驱动器已经没了。排查手段就一条先执行Get-PSDrive确认当前存在哪些驱动器再检查 Root 路径是否拼写正确。第二类比较诡异报错是cd : 无法将“set-location”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。这个报错经常让人摸不着头脑。我遇到过的情况通常有两种一种是在某些被修改过的执行环境里Set-Location这个命令解析被影响了另一种是有人定义了名为cd的函数把内置 cd 函数遮蔽了。先执行Get-Command Set-Location -All看看真实命令是否还在。如果只是个案直接尝试get-help Set-Location或重启 PowerShell 通常能解决。如果确定是被同名函数遮蔽可以用Microsoft.PowerShell.Management\Set-Location这种模块限定全名绕过。5.2 网络驱动器“假死”与清理带-Persist的文件系统驱动器本质是一个 Windows 映射网络驱动器。一旦服务器关机、网络断开或者共享被移除这个驱动器就处于假死状态你在资源管理器里看它还在但双击进去转圈半天最后报找不到网络路径。这时候不要硬等。在 PowerShell 里把当前路径切回 C 盘然后删除它cd C:\ Remove-PSDrive -Name Data -ErrorAction SilentlyContinue如果Remove-PSDrive还提示驱动器占用或者根本删不掉就回到传统管理工具处理net use Data: /delete /y然后重新创建New-PSDrive -Name Data -PSProvider FileSystem -Root \\server\share -Persist -Credential (Get-Credential)注意-Credential参数在处理需要身份验证的共享时很关键。文件系统提供程序支持凭据传入这样你就不必在资源管理器里手动输入网络凭据脚本也能顺利访问受保护共享。但 Registry、Certificate 这些提供程序就不支持 -Credential权限控制走的是当前进程令牌。5.3 PowerShell 中文乱码很多人在 Windows PowerShell 5.1 里跑脚本发现中文文件名、中文输出全是乱码。这个问题的根源在于编码模型不一致。Windows PowerShell 5.1 是建立在 .NET Framework 之上的默认输出编码跟随系统代码页中文系统常见的是 GBK / GB2312而 PowerShell 7 默认就是 UTF-8。如果你的脚本文件保存成 UTF-8 无 BOM5.1 会按 ANSI 代码页去读中文直接变天书。遇到乱码先处理控制台编码[Console]::OutputEncoding [System.Text.Encoding]::UTF8 $OutputEncoding [System.Text.Encoding]::UTF8脚本文件本身要按照 UTF-8 with BOM 保存至少在 Windows PowerShell 5.1 里必须这样。如果你用 VS Code在右下角编码按钮里选“通过编码保存”选 UTF-8 with BOM 即可。如果脚本要长期维护我建议直接换成 PowerShell 7编码问题少一大半。5.4 “此电脑-设备和驱动器”里的多余图标与盘符这个其实不算 PowerShell 提供程序的问题但因为长得像经常被混在一起问。资源管理器左侧和“此电脑”页面里的“设备和驱动器”是 Windows Shell 的展示层里面出现的网络盘、U盘、光驱以及爱奇艺这类厂商图标都来自系统挂载状态和 Shell 扩展。如果你是看不惯“此电脑”里一堆第三方厂商的图标比如某播放器、某网盘这通常是通过注册表 CLSID 注册的 Shell 命名空间扩展挂进去的。可以在注册表里找这个位置HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace下面每个子键都是“此电脑”页面里显示的一个项。想隐藏哪个就找到对应 CLSID 然后删掉那个子键。删之前建议先把整个子键导出备份因为不同图标对应的 CLSID 很难一眼认出来。至于隐藏某个正常盘符更推荐用组策略里的“隐藏‘我的电脑’中这些指定的驱动器”而不是去改 NoDrives 那个位掩码 DWORD后者一旦算错能把整排盘符都搞没。最后分享一点实际体会我不太喜欢用“总结”这种收尾方式就说说我自己的使用习惯吧。提供程序这个东西刚开始接触时容易觉得抽象但一旦你习惯用Cert:查证书、用HKLM:逛注册表、用Env:看环境变量你就很难再退回到鼠标去一层层点开 regedit 或者证书管理单元。我个人的做法是把常用的映射写进$PROFILE每次打开终端自动挂好配合 Get-PSDrive 的扫描逻辑很多日常排查五分钟内就能完成。另外有个扩展小建议你完全可以把这套思路复制到自己的管理脚本里比如写一个模块在导入时自动挂载常用路径的驱动器卸载时自动移除配合Register-PSDrive相关命令团队内部复用会非常方便。最后提醒一句-Persist参数只对 FileSystem 提供程序有意义别在注册表路径上期待它会存到注册表里那是两个世界的持久化。
