☰
psa.cmd:在地址栏一键打开当前目录的管理员 PowerShell 7
2026/10/14 21:44:09 网站建设 项目流程

最近半年我养成了一个习惯:在文件资源管理器里定位到某个项目目录后,顺手按Ctrl+L聚焦地址栏,输入psa回车,等 UAC 弹窗通过,一个带管理员权限的 PowerShell 7 窗口就落在当前目录了。整个过程大概三秒钟,比原来的"开管理员终端 → 复制路径 → 切目录"三步操作快得多,也省心得多。

这个psa.cmd不是什么高深技术,就是一个小批处理脚本,但把它吃透之后,你会顺带搞清楚 Windows 资源管理器地址栏的执行机制、UAC 提权后工作目录为什么会跑飞、以及 PowerShell 7 和系统自带的 Windows PowerShell 到底有什么区别。这篇文章就围绕这个脚本展开,从使用场景、底层原理、源码拆解到部署避坑,完整记录下来,适合所有在 Windows 11 上做开发、运维、系统调优的人参考。

1. 为什么非要有 psa.cmd:一个每天重复几十次的别扭动作

先交代一下痛点来源。我日常大量时间在跟各种项目目录打交道,频繁要做的事就是在某个目录下执行一些需要管理员权限的命令,比如操作 Windows 服务、改 hosts、调整注册表、跑一些需要提权的安装脚本。Windows 11 本身提供了不少打开管理员终端的方式,但真到了"我人就在某个目录里,想立刻在当前目录下获得管理员 PowerShell"这个场景,默认操作链路其实是相当别扭的。

最常见的做法是Win + X打开"终端(管理员)",这时候弹出来的窗口工作目录几乎总是C:\Windows\System32。接下来你得重新定位到刚才那个目录——路径短还好,路径一长就得复制粘贴,而且终端里粘贴包含空格和特殊字符的路径时还容易翻车。第二种做法的变形是先在地址栏输入powershell或pwsh,直接开一个落在当前目录的 PowerShell,但这个进程是普通权限,一旦碰到需要管理员权限的命令,又得重新折腾一个提权窗口,前面定位目录的工作全白费。第三种是用普通终端启动后,再通过Start-Process -Verb RunAs提权,工作目录同样会丢。

这三个方案来回切换多了,我就确定了一件事:我需要一个"一条命令同时搞定三件事"的入口——以管理员身份启动、使用 PowerShell 7、工作目录直接对应当前资源管理器所在目录。于是就有了psa.cmd,名字取自 PowerShell Administrator 的缩写。

1.1 传统开管理员终端的几种姿势,各有各的窝火

把这几种方式摆在一起看,问题就非常清楚了。

方式操作步骤当前目录是否正确是否提权体验问题
Win+X 选择终端(管理员)2 步否,落在 System32是每次都要重新 cd 到目标目录
地址栏输入 pwsh 回车1 步是否权限不够时白开
先开普通终端再提权3-4 步否,提权后丢失是操作繁琐,路径要再粘贴一遍
运行窗口输入 runas3 步否是需要输入密码,不适合日常开发

其中最让人恼火的是"提权后目录丢失"这个问题。Windows 10 和 Windows 11 的生态里,很多工具(比如 Windows Terminal)已经做了一些改进,能从资源管理器右键菜单里继承目录,但当你走 UAC 提权链路时,这个继承关系大概率是断掉的。一开始我以为是自己姿势不对,后来才明白这其实是 Windows 安全模型下的一个固有现象,后面我会专门讲底层原因。

1.2 地址栏还有一个容易被忽略的"命令入口"属性

很多人的印象里,文件资源管理器地址栏是用来显示路径、输入路径跳转的,最多再输入个cmd在黑窗口里跑命令。但它的真实能力不止于此:地址栏本质上是一个"既能解析路径,又能解析命令"的输入框。你输入的东西如果匹配一个存在的路径,它负责跳转;如果匹配不到路径,它会把输入交给操作系统的命令执行机制去处理。

这意味着你可以把任意一个放在PATH环境变量里的可执行程序或脚本名直接敲进地址栏运行。很多人不知道的是,地址栏输入cmd能打开命令提示符且当前目录就在资源管理器位置,输入powershell能打开 Windows PowerShell,输入wt能打开 Windows Terminal——这些都是同一个机制在起作用。psa.cmd本质上就是把你自己的脚本变成了这个机制里的一个"合法命令",从今往后在地址栏里敲psa就像敲cmd一样自然。

2. 三个底层机制搞懂之后,你再也不会觉得这是黑魔法

实话说,这个脚本不复杂,核心逻辑不到十行。但如果你不理解背后三个机制,你可能照着装上能用,遇到一点偏差就抓瞎。我把它们拆开讲清楚。

2.1 Explorer 地址栏执行命令时,当前目录从哪来

地址栏输入cmd回车,为什么新开的命令提示符窗口恰好落在当前文件夹?这里的关键在于资源管理器在创建外部进程时,会把当前文件夹路径作为新进程的初始工作目录传进去。对应到 Windows API 层面,就是用ShellExecuteEx执行外部命令时,会附带当前目录作为lpDirectory。所以对于 .cmd 脚本来说,脚本里的%CD%动态变量就等于资源管理器当前所在目录。

这一点是整个脚本的基石。你想让目标 PowerShell 窗口落在哪个目录,根本不需要用户手动告诉脚本,也不需要复制路径,只要用户"先导航到目标目录,再执行 psa.cmd"就足够了。脚本从进程环境里自动取到当前目录,剩下的工作就是把这个目录传递给提权后的新进程。

2.2 UAC 提权为什么总是让目录飞到 System32

这是 Windows 上最容易让开发者困惑的问题之一:明明我用普通权限启动了一个进程,用Start-Process -Verb RunAs弹 UAC 提权之后,新窗口的工作目录却变成了C:\Windows\System32,而不是继承原来那个进程的工作目录。

原因是提权进程的创建路径并不那么"直线"。当你触发 UAC 提权时,实际流程是:当前进程把请求交给系统的 AppInfo 服务,AppInfo 弹出 consent.exe 确认对话框,确认通过后由系统以高完整性级别创建一个新进程。这个新进程并不是在当前进程的上下文里直接 fork 出来的,因此调用方设置的初始工作目录不会可靠地传递过去,系统默认给了System32。

这个设计有一定的安全考虑,但对我们这种普通用户来说,实际体验就是"提权等于丢目录"。解决办法也很直接:既然系统不帮你传目录,你就自己想办法把目录信息作为参数带进新进程,进入 PowerShell 后再用Set-Location切过去。psa.cmd 里的核心逻辑,说白了就是"先记下当前目录,提权后再切回去"。

2.3 为什么不直接写 powershell.exe,非要绕到 pwsh

Windows 11 系统自带的是 Windows PowerShell 5.1,可执行文件是powershell.exe。而我们要打开的是 PowerShell 7,可执行文件是pwsh.exe,它们两个在系统里是并存的独立组件,用哪个取决于你到底想开哪个。很多人混用这两个概念,以为"PowerShell"只有一个,其实从 7.0 开始,PowerShell 已经成为一个独立于 Windows 系统的可再分发组件,所以启动命令也变了。

pwsh.exe默认是不存在于系统盘 Windows 目录里的,它是通过安装包或包管理器装到独立目录,并在安装时把路径写入系统 PATH。因此脚本里调用pwsh能成功,前提是你的 PATH 里有它。如果你机器上装了 PowerShell 7 却在脚本里用powershell,那开出来的只会是 5.1 的旧窗口,标题、功能和默认行为都会不对。这里我踩过一次坑,后面会在避坑章节详细说。

3. psa.cmd 的源码与逐行讲解

下面给出我目前在用的正式版脚本,它已经处理了大部分路径特殊字符的问题,实测在各种项目目录下都能稳定工作。

3.1 正式版源码

@echo off setlocal set "PSA_START_DIR=%CD%" powershell -NoProfile -WindowStyle Hidden -Command "$d=$env:PSA_START_DIR; Start-Process pwsh -Verb RunAs -ArgumentList @('-NoExit','-Command',\"Set-Location -LiteralPath '$d'\")"

把这段代码存为psa.cmd就得到了最核心的版本。你可能注意到这里绕了一下:先用系统自带的powershell.exe作为"启动器",再让它去弹 UAC 并启动pwsh.exe。为什么不直接在 .cmd 里用runas或者别的命令?因为从 .cmd 进程里直接触发 UAC 提权最干净的方式,就是调用 .NET 的Start-Process,而系统自带的 Windows PowerShell 正好能干这件事且不需要额外安装,用-WindowStyle Hidden可以让这个充当启动器的黑色窗口一闪而过,不会让用户看到两个窗口先后冒出来。

3.2 每一行在干什么

逐行拆开看,这个脚本的逻辑其实非常直白:

  • @echo off:关闭命令回显,避免脚本执行时在启动器窗口里滚动输出一堆命令文本。
  • setlocal:让脚本内置的环境变量在退出后自动销毁,不会污染当前命令会话。
  • set "PSA_START_DIR=%CD%":把当前工作目录(也就是资源管理器所在目录)保存到环境变量PSA_START_DIR。这里用引号包裹赋值语法,能正确处理路径中含空格的情况。
  • powershell -NoProfile -WindowStyle Hidden -Command ...:调用系统自带的 Windows PowerShell 执行一段启动命令。-NoProfile避免加载用户配置文件,减少变量污染和启动时间;-WindowStyle Hidden让这个中间进程不显示窗口。
  • $d=$env:PSA_START_DIR:在这个启动器 PowerShell 进程里,把环境变量值读入$d变量。
  • Start-Process pwsh -Verb RunAs -ArgumentList ...:启动pwsh.exe,-Verb RunAs表示请求 UAC 提权。传给它的参数是-NoExit(保持窗口不退出)和-Command(进入后执行切换目录命令)。
  • "Set-Location -LiteralPath '$d'":在提权后的 PowerShell 7 窗口里,把当前目录切换到$d。用-LiteralPath而不是普通路径参数,是为了避免路径里的方括号等通配符被解析。

这整个链条完成的事情就是:拿到当前目录 → 放进环境变量 → 提权启动 pwsh → 在 pwsh 里根据环境变量切目录。看似多绕了一层,但实际上每一环都避开了下一个环节的坑。

3.3 一条更朴素的写法,以及它为什么不稳定

在写出上面这个版本之前,我先试过一个更"直给"的版本:

@echo off powershell -NoProfile -Command "Start-Process pwsh -Verb RunAs -ArgumentList '-NoExit','-Command','Set-Location -LiteralPath \"%CD%\"'"

这段脚本在 90% 的场景下都能用,但它对路径中的特殊字符非常敏感。路径里如果含有&、括号、单引号等字符,命令在层层转义的过程中很容易碎掉。典型翻车场景是"你的项目目录名里带&",比如Design & Docs,这种路径听起来少见,但在真实工程里并不稀奇。另外,如果目录名里恰好有英文单引号,上面这版几乎必炸。为了避免这种"看起来能用、一碰特殊情况就死"的脆弱状态,我才换成了把路径放进环境变量、由提权后的 PowerShell 自己读取的方案。环境变量的传递方式是进程级别的,不需要做嵌套引号转义,稳定性高很多。

4. 部署与使用:把 psa 变成肌肉记忆

脚本写好了,但想让它在地址栏里三秒启动,还得做好两件事:一是把脚本放在一个能被找到的位置,二是让 Windows 知道去哪里找它。下面这段部署经验是我实际测试过的,跟着做不会出错。

4.1 脚本放哪、PATH 怎么配

我推荐专门建一个存放个人工具脚本的目录,比如C:\Users\你的用户名\bin或D:\Tools\bin。把psa.cmd放进去之后,打开系统设置里的"编辑环境变量",在用户变量里找到Path,新增一行指向这个目录。设置好之后,重新打开文件资源管理器让它重新读取环境变量,就能在任意位置通过地址栏输入psa来调用脚本了。

这里有两点要注意。第一,设置完 PATH 后,已经打开的资源管理器窗口可能不会立刻刷新环境变量,保险起见重新开一个窗口再试。第二,地址栏输入psa会不会跟某个同名文件夹冲突?如果当前目录下恰好有个叫做psa的文件或文件夹,资源管理器会优先把它当作路径来处理,导致命令不执行。实际遇到这种情况的概率很低,但你要知道这个优先级关系。

4.2 地址栏的三种调用方式

部署好之后,你有三种方式可以调用它:

  1. 聚焦到任意文件夹,按Ctrl+L让地址栏进入可编辑状态,输入psa回车。
  2. 在地址栏里输入psa.cmd回车,效果一样,多打了 4 个字符。
  3. 在当前文件夹上方的"路径"区域点击空白处,直接输入psa,本质上也是聚焦地址栏。

我最常用的是第一种。按Ctrl+L是很多 Windows 用户的盲区,但配合这个脚本确实非常顺手,相当于资源管理器里的一个"隐藏快捷键"。

4.3 顺手做一套配套变体

只用了一个星期,我就发现光有psa还不够,很多时候我只是想在当前目录快速开一个普通权限的 PowerShell 7,于是补了一个psc.cmd。它的内容更短:

@echo off cd /d "%CD%" start "" pwsh -NoExit

cd /d "%CD%"用来确保起始目录正确,然后start "" pwsh -NoExit打开 PowerShell 7 窗口。这个普通版的好处是没有 UAC 弹窗,适合日常查看文件、跑普通命令。如果你还需要在管理员窗口里保持目录,那就继续用开启管理员判断的进阶版本,这个我在踩坑章节结尾会给出方案。

5. 实测结果与新手最容易踩的五个坑

脚本写完之后,我把它扔给了几位同事和同学试用,反馈大致是"好用是好用,但为什么我装了以后遇到的第一个问题跟你的教程对不上"。我复盘了一下,把新手最容易踩的坑集中列出来,按出现频率从高到低排序。

5.1 最容易踩的五个坑

  • 找不到 pwsh,脚本报了错但没有任何窗口弹出来:这通常意味着 PowerShell 7 没装,或者装完之后 PATH 没有正确写入。先在任意终端执行where.exe pwsh,如果找不到,重新安装 PowerShell 7 再试。
  • UAC 弹窗消失了,或者提示操作被管理员禁止:你当前登录的用户可能不是管理员账户,或者所在组织通过组策略限制了提权操作。这个脚本依赖标准 UAC 流程,遇到这种环境只能另想办法。
  • 窗口确实弹出来了,但目录停在 System32:十有八九是脚本写成了我前面说的"朴素版",在路径含特殊字符时失效。换成环境变量版即可。
  • 明明输入了 psa,回车却发现资源管理器只是在当前目录里选中了某个叫 psa 的东西:这是地址栏路径优先级的正常现象,换到没有同名对象的目录再试即可。
  • 第一次运行脚本时杀毒软件或 SmartScreen 跳提示:自己创建的本机脚本一般不会触发,但如果这个文件是从网上下载的,可能带有网络标记,右键打开属性里"解除锁定"之后再运行就行。

这五个坑每一个我都单独花过时间排查。最搞笑的是第一个坑,我当时在同事电脑上装完脚本,怎么敲 psa 都没反应,最后发现他装了 PowerShell 7 但 PATH 里没有 pwsh,而我一直默认 pwsh 全局可用。所以现在我把"先验证 pwsh 可执行"写在了部署清单第一条,宁可在前面多花半分钟,也不要等到弹空窗再猜。

5.2 路径含特殊字符时的终极兜底方案

环境变量版已经覆盖了空格、&、括号、百分号等绝大多数情况。但如果你是那种在项目目录里用单引号命名的狠人,比如John's Project,那环境变量版里的单引号包裹仍会出问题。遇到这种极端命名,我建议直接用 PowerShell 的-EncodedCommand来传递指令,把命令编码成 Base64 字符串后作为一个不包含任何引号问题的参数传过去。

具体做法可以先在 PowerShell 里构造编码命令:

$cmd = "Set-Location -LiteralPath '" + $env:PSA_START_DIR + "'" $enc = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd)) Start-Process pwsh -Verb RunAs -ArgumentList '-NoExit','-EncodedCommand',$enc

这样路径里无论出现什么字符,都会被编码成一个纯字符串,不可能产生解析冲突。不过这个方法对于普通使用场景来说属于"杀鸡用牛刀",我把它放在这里主要是提供一个思路:当你发现引号、转义怎么调都不对时,就想想能不能把命令本身编码掉,而不是继续跟嵌套引号搏斗。

6. 一点延伸:让"当前目录 + 管理员终端"的组合应用到更多场景

psa.cmd 解决的虽然是一个很小的痛点,但它背后那条"通过地址栏快速拉起自定义命令,并保持当前目录上下文"的思路,可以被复制到很多地方。

6.1 如果你日常用的其实是别的 Shell

如果你在 Windows 上用的是 CMD、Git Bash、WSL 或各种编程语言自带的交互终端,这套思路一样能搬过去。地址栏对可执行程序的查找和启动机制是通用的,区别只在于目标程序对"初始工作目录"的支持方式不同。比如想开一个普通权限的 CMD 窗口落在当前目录,地址栏输入cmd就行;想开 Git Bash,可以做一个gitbash.cmd,用cd /d "%CD%" && 启动的方式把目录带过去;想打开 WSL 并落在当前目录,同样可以在批处理里先cd再调用wsl.exe。核心就是记住一句话:在 Windows 里,你几乎总是可以通过一个薄薄的批处理壳,把"当前目录"这个关键状态传递给任何目标程序。

6.2 把它接到 VS Code 等集成终端里

另外一个高级用法是给 VS Code 配置一个自定义终端入口。VS Code 在资源管理器目录上右键可以直接"通过 Code 打开",本来就能继承当前目录。但如果你想在启动 VS Code 时顺便打开一个管理员终端,可以把 psa.cmd 的思想整合到 Code 的任务配置或快捷键里,让编辑器和终端同时从同一个目录上下文启动。我实际试过给 Code 配置一个带提权终端的任务,效果非常稳定,相当于把"在这个项目里开一个管理员终端"也变成了一键操作。

6.3 给 psa.cmd 加一个"已经管理员就不弹 UAC"的优化

最后分享一个我后来加的小优化。如果你已经在一个管理员终端里,又运行了 psa.cmd,它会再弹一次 UAC 确认,稍显多余。我给脚本加了一个判断:先探测当前进程是否已经有管理员权限,有的话直接在当前会话里切目录启动 pwsh,没有的话才走 UAC 链路。探测方法用系统自带的net session就能实现,无权限时这条命令会报错,退出码非零;有权限时退出码为零。由此可以把 psa.cmd 升级为"自动判断是否需要提权"的版本,体验更顺滑。

我在实际使用中的体会是,这类小工具最怕的不是功能做不到,而是"大多数情况下能用、少数情况下莫名失效"。把几个关键机制搞懂之后,你就能在问题发生时第一时间判断是 PATH 问题、目录优先级问题、还是提权链路的固有限制,而不是对着屏幕干瞪眼。这个脚本虽然只有几行,但每次在地址栏里敲下 psa 的瞬间,我都会觉得当初折腾这几个小时非常值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询