刚接触PowerShell的人几乎都会在第一次跑脚本时被同一条报错骂回来“因为在此系统上禁止运行脚本”。后面往往还跟着一长串关于set-executionpolicy的英文提示很多人第一次看到直接懵了去搜索引擎一查答案五花八门有让改注册表的有让用cmd绕过的还有让把执行策略直接改成Unrestricted的。其实这个问题没那么玄乎根子上就是一道Windows的安全闸门——执行策略Execution Policy。这篇文章我不打算只给一条命令让你复制粘贴而是把这道闸门的构成、优先级、不同场景下的开法以及改完之后仍然报错的那些真实原因一条一条拆清楚。不管你是被脚本困扰的新手还是帮同事排查问题的老手看完之后应该能少走很多弯路。1. 报错现场还原“禁止运行脚本”到底是谁在拦你1.1 这个报错的完整样貌同一个问题在不同场景下冒出来的文本不完全一样。最常见的是这样无法加载文件 C:\Users\admin\test.ps1因为在此系统上禁止运行脚本。有关详细信息请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。英文系统下则显示File C:\Users\admin\test.ps1 cannot be loaded because running scripts is disabled on this system.注意这里有两层信息第一层是明说了“禁止运行脚本”第二层是让你去查about_Execution_Policies。也就是说系统非常明确地告诉你不是你的脚本坏了也不是PowerShell坏了而是执行策略压根不允许当前脚本运行。1.2 执行策略是什么Windows给你装的一道“应用安装拦截”你可以把执行策略理解成智能手机上的“未知来源应用安装拦截”。你在手机设置里允许了某个应用商店之外的应用安装不是因为这个应用本身没问题而是你选择信任当前这个来源同理执行策略决定了PowerShell是否允许加载.ps1脚本以及允许加载哪些来源的脚本。Windows默认把这道闸关得很紧。常见的执行策略有五种分别代表由严到松的信任级别策略名称含义典型适用场景Restricted禁止运行任何脚本交互式命令不受影响Windows客户端默认配置AllSigned所有脚本必须经过受信任发布者签名才能运行安全要求极高的生产环境RemoteSigned本地创建的脚本可运行从网络下载的脚本必须签名大多数开发机、服务器的推荐配置Unrestricted所有脚本都可运行但从网络下载的脚本会弹出确认提示个人学习、受控测试环境Bypass不做任何检查什么都不拦临时一次性执行、自动化部署脚本Windows 10和Windows 11的客户端系统默认执行策略就是Restricted也就是“一个脚本都不放行”。所以你打开PowerShell去敲.\xxx.ps1系统当然直接拒绝。这不是什么故障恰恰是它履行了保护职责。1.3 顺手验证一下你的电脑当前是什么策略不用猜直接执行这条命令Get-ExecutionPolicy输出结果是Restricted那你就是撞上了默认配置如果是RemoteSigned或AllSigned说明之前有人手动开过闸。如果输出是Undefined表示没有设置任何有效策略此时系统会按默认配置执行也就是视同Restricted。这里要专门提醒一句我见过不少人拿这条命令跑完发现Restricted也不管三七二十一直接改成Unrestricted然后完事。这种做法极其不推荐。因为Unrestricted意味着所有脚本都能运行万一你未来某天双击了一个下载来的恶意.ps1系统不会提示你任何风险。安全边界这东西平时感觉不到真被突破一次就够难受的。2. 执行策略的五层优先级为什么你改了还是没用很多人在排查这个问题时都卡在同一个地方明明执行了Set-ExecutionPolicy RemoteSigned也提示成功了可一跑脚本还是报“禁止运行脚本”。这时候你需要的不是重新找一条命令而是先弄清一个概念——执行策略不只一层。2.1 五个作用域就像五把环环相扣的锁PowerShell的执行策略分为五个作用域Scope从高到低分别是MachinePolicy— 计算机组策略由域管理员通过组策略下发UserPolicy— 用户组策略同样由组策略下发但只作用于当前用户Process— 仅对当前PowerShell进程生效进程一关就失效CurrentUser— 写入当前用户的注册表配置LocalMachine— 写入本机注册表对机器上所有用户生效它们的优先级是严格按照上面这个顺序来的。也就是说MachinePolicy永远压过UserPolicyUserPolicy永远压过Process依此类推。策略生效时取的是最高优先级里“有定义”的那一个而不是多个作用域取并集或者取交集。2.2 一条命令看穿所有层级想一次性看清每个作用域的策略值用这个Get-ExecutionPolicy -List输出大概长这样Scope ExecutionPolicy ----- --------------- MachinePolicy Undefined UserPolicy Undefined Process Undefined CurrentUser RemoteSigned LocalMachine Undefined这种情况下生效的是CurrentUser的RemoteSigned。但如果你看到的输出是下面这样MachinePolicy AllSigned CurrentUser RemoteSigned那就意味着即便你把CurrentUser改成了RemoteSigned最终生效的仍然是MachinePolicy的AllSigned——因为顶部的组策略压过了下面所有层。这就是“改了没生效”最常见的原因之一。2.3 另一个隐蔽原因32位和64位PowerShell各有一份策略还有一个特别容易踩的坑就是32位和64位PowerShell的执行策略是分开存储的。你在64位的PowerShell里运行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser只改了64位那份配置如果你用32位PowerShell路径通常在C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe去跑同一个脚本它读到的仍然是Restricted于是继续报错。这种情况常见于某些旧版软件、数据库管理工具内部调用的PowerShell组件它们很可能是32位进程。排查思路很简单在报错的程序里确认一下当前的执行策略值如果有怀疑就在32位PowerShell里也执行一遍设置命令。# 在32位PowerShell中执行 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser3. 按场景拆解法不同情况下这道闸该怎么开既然原因清楚了下面直接上实操。我不给“万能命令式”的一刀切方案而是按你具体遇到的情况给出对应的处理路径。3.1 场景A你自己电脑上想允许跑本地脚本这是最普遍的需求你在自己的开发机或办公电脑上写了一堆.ps1脚本希望一键运行但不希望网络上下载的脚本随意执行。推荐配置是RemoteSigned作用域选CurrentUser这样不会影响机器上的其他用户Set-ExecutionPolicy RemoteSigned -Scope CurrentUser命令执行后会弹出一个确认提示输入Y回车即可。如果想跳过确认可以这样写Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force设置完之后验证一下Get-ExecutionPolicy看到RemoteSigned就成功了。此时本机创建的.ps1文件全部可以直接运行从网上下载的脚本如果带“来自Internet”的标记Windows仍然会拦下并提示需要解除锁定。这个标记的细节我放到第5章专门讲因为太容易踩了。3.2 场景B系统服务或任务计划里跑脚本需要更高权限如果你要在计划任务里跑PowerShell脚本服务账户执行的进程和你手动开的窗口不是同一个会话。服务账户读到的执行策略同样受组策略和本机策略约束。这种场景下两个可选的思路思路一全局放开本机策略# 必须以管理员身份运行PowerShell Set-ExecutionPolicy RemoteSigned -Scope LocalMachine如果提示“不是以管理员身份运行”或“拒绝访问”你需要右键PowerShell图标选择“以管理员身份运行”再执行一次。思路二在调用时指定参数绕开策略限制如果你不希望放开全局策略可以改你的任务计划或者启动脚本直接在启动命令里带上执行策略参数powershell.exe -ExecutionPolicy Bypass -File D:\scripts\deploy.ps1这种做法的好处是不改动注册表、不影响系统全局配置、仅对本次调用有效。缺点是你得保证启动命令本身可控别在别人的入口上硬塞一个Bypass那就等于把门拆了。3.3 场景C只想跑一次不想留任何配置痕迹有时候你就想临时跑一个脚本看效果不想在系统里留下任何策略变更记录。这种情况建议用进程级作用域Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass这个命令只在当前PowerShell窗口内有效关闭窗口后自动恢复原状。当前窗口内的任意脚本都能跑不会污染注册表。也可以不进入PowerShell直接在CMD里一次性调用powershell -ExecutionPolicy Bypass -Command C:\scripts\test.ps1手动执行时这招很实用尤其是你不想答“是否更改执行策略”那个交互提示的时候。3.4 场景D管理员要批量管几十台机器如果你是运维需要给一批机器统一放行脚本手工一台台敲命令显然不现实。这时可以考虑组策略推送。在Windows Server的组策略管理器中找到计算机配置 → 管理模板 → Windows 组件 → Windows PowerShell → 打开脚本执行将该项设置为“已启用”然后在“执行策略”下拉框里选RemoteSigned或AllSigned最后在整批机器上刷新组策略gpupdate /force刷新之后组策略会写入MachinePolicy或UserPolicy作用域这个值优先级最高用户自己在机器上怎么改都覆盖不了对统一管理有好处。但也要注意如果你不希望用户自行修改策略就别把LocalMachine和CurrentUser设为宽松值否则组策略压不住了。3.5 场景E公司内部已有代码签名证书想完全规范化企业环境下如果已经配置了内部代码签名证书可以将执行策略设为AllSigned。这个配置比RemoteSigned更严格本地脚本如果没有受信任签名默认也不会运行。想给现有脚本签名可以用Set-AuthenticodeSignature。这个过程需要代码签名证书个人开发者如果没有证书就不太适合用这个方案。一般中小企业建议RemoteSigned已经足够没必要为了形式上的“更安全”把自己折腾坏。3.6 通常不建议做的事直接 Unrestricted我前面提过一遍这里再说一次把执行策略改成Unrestricted是最快的解法但也是后患最大的解法。因为它把所有来源的脚本都放开了等于彻底失去筛选能力。你如果只是因为“跑一个脚本卡住了”就去开这个门迟早会后悔。尤其是在开发环境里装了各种工具链的机器谁知道哪个下载的安装脚本里藏了什么。靠谱的最小权限原则是能用RemoteSigned就不用Unrestricted能用CurrentUser就不动LocalMachine能用临时调用参数就不改注册表。4. 另一个高频报错“无法将xxx项识别为 cmdlet”不是执行策略问题收到“因为在此系统上禁止运行脚本”的报错大家都会想到执行策略。但实际工作中流传度同样高的一条报错也和PowerShell脚本有关那就是无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写如果包括路径请确保路径正确然后再试一次。很多人一看到“脚本文件”四个字就以为和执行策略有关跑去改Set-ExecutionPolicy改了半天还是不行。这两个报错根本不是一回事别认错了方向。4.1 两类报错的本质区别“禁止运行脚本”是策略拦截系统是“能识别但故意不让你跑”。“无法将xxx识别为 cmdlet”是命令查找失败系统是“压根找不到这个东西”。最直接的判断方法是看报错里有没有“系统禁止运行”这几个字有“禁止运行脚脚本” → 执行策略问题有“无法识别为 cmdlet” → 命令路径或环境变量问题4.2 命令找不到通常只需要检查这四个环节第一确认软件是否真的装了。比如报错里提到npm你先在CMD里敲npm -v如果CMD里能用、PowerShell里不能用问题多半出在环境变量同步上。如果CMD里同样不能用那就是装都没装好。第二检查PATH环境变量。npm这类工具安装时一般会把安装路径写入PATH。如果安装路径没加进去PowerShell自然找不到。可以在PowerShell里查看$env:Path找一下有没有npm所在的目录比如C:\Program Files\nodejs\。没有的话手动把该目录加到PATH里。注意修改完后要重新开一个PowerShell窗口因为当前窗口不会自动加载新的环境变量。第三检查工具是否有独立的cmd版本。很多Windows工具像npm、mvn、git实际安装后会生成一个npm.cmd或mvn.cmd。PowerShell遇到.cmd文件在有些机器上会因为$env:Pathext配置不全而找不到。这种情况可以尝试直接敲一次全名npm.cmd -v如果这样能用说明PowerShell没把.cmd当作可执行文件的扩展名需要检查$env:Pathext是否包含.CMD。第四考虑重新初始化当前会话。如果你刚装完软件当前PowerShell窗口可能还没加载新PATH。可以重新开一个窗口或者用refreshenv这个命令在 Chocolatey、nvm-windows 等工具环境下比较常见如果系统没定义这个命令直接关掉重开PowerShell窗口即可。5. 远程下载的脚本为什么特别顽固Mark of the Web 和解除锁定5.1 下载来的脚本总是加载不了用浏览器从网上下载一个.ps1文件放到本地后右键属性你可能会在“常规”选项卡底部看到一行小字“安全此文件来自其他计算机可能被阻止以帮助保护该计算机。”这就是Mark of the WebMOTWWindows会把“来自Internet”的标记写入文件的备用数据流中。这个标记存在时即使执行策略已经设为RemoteSigned也会出现前面提到的那个“禁止运行脚本”报错。因为RemoteSigned的逻辑是本地创建的脚本可运行从网络下载的脚本必须有签名。即便你只是在浏览器里点了一下下载没有运行Windows也已经给文件打上了“来自Internet”的标。5.2 最简单的解除方式第一种文件右键 → 属性 → 勾选“解除锁定” → 确定。这会让Windows移除该文件的MOTW标记。第二种在PowerShell中批量解除某个文件夹下所有脚本的锁定Get-ChildItem -Path C:\scripts\*.ps1 | Unblock-FileUnblock-File会移除文件上所有用Zone.Identifier保存的Internet来源信息。解除之后再运行脚本RemoteSigned策略下就不会再拦你了。5.3 这个标记和“禁止运行脚本”混在一起的经典场景Git仓库里clone下来的文件一般不会带MOTW标记因为Git客户端写入文件时不经过浏览器的MOTW逻辑。但用网盘、邮件附件、IM传输收到的.ps1文件十有八九都带Internet标记。所以你会遇到一个看起来很矛盾的现象自己本地写脚本能跑从同事微信里发过来一个脚本就怎么都跑不了。本质不是同事的脚本有毒而是Windows在严格执行“来源不可信”的判定。在开发环境里建议把下载的脚本保存到固定目录后统一执行一遍Unblock-File再进入后续调试能省掉很多“明明策略没问题就是跑不了”的排查时间。6. 改完策略后仍然报错的几种“查不出原因”的情形这一节里我集中说几个不太常见、但真上线时很容易卡壳的情形都是实际踩过坑总结出来的。6.1 以管理员身份运行了但 CurrentUser 还是改不动部分场景下明明开启了管理员PowerShell输入Set-ExecutionPolicy RemoteSigned -Scope CurrentUser却又弹“拒绝访问”或者“没有注册表项”之类的提示。这种情况最常见的原因是当前用户不是管理员甚至不在本地管理员组里而你打开PowerShell时用的“管理员”是其它的域账号。处理方式很简单换一个真正属于本机管理员的账号来操作或者用-Scope LocalMachine试试因为本机范围只要求当前进程有管理员令牌不需要和用户注册表绑定那么紧密。6.2 策略已经是 RemoteSigned本地脚本还是被拦如果Get-ExecutionPolicy明明返回RemoteSigned运行本地脚本仍提示禁止运行先别急着怀疑策略没生效。按这个顺序排查脚本文件是否带有MOTW标记右键属性看锁定项。系统里是否还开着特权区域如C:\Windows、C:\Program Files下的受限脚本执行。当前的PowerShell主进程是不是被某个外部工具注入了特殊的策略比如一些统一终端平台会在后台拉起进程时自带-ExecutionPolicy Restricted参数。是否使用了VS Code的终端VS Code可能通过pwsh或powershell启动时带了额外参数覆盖了默认策略。第4点尤其隐蔽。因为你在VS Code里开了终端敲Get-ExecutionPolicy看到的可能是正常值但实际执行脚本时VS Code集成终端的配置项terminal.integrated.shellArgs.windows或任务配置里的command带有-ExecutionPolicy参数直接压过了你的设置。此时需要检查VS Code的settings.json或.vscode/tasks.json看看有没有多余的启动参数。6.3 在Jenkins、GitLab Runner里跑脚本总报错CI/CD流水线里跑PowerShell经常会遇到同样的问题本地能跑一到构建机上就报“禁止运行脚本”。因为CI的Agent服务通常是以一个服务账号运行的这个账号的CurrentUser作用域里压根没有设置执行策略而LocalMachine又受限于系统默认值。最稳定的做法不是在流水线里写一堆Set-ExecutionPolicy也不是靠某个开发机的“运气”配置而是在构建机上用管理员身份设置RemoteSigned并加上LocalMachine作用域Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force或者更推荐的是在CI任务里直接指定启动参数powershell.exe -ExecutionPolicy Bypass -File build.ps1这样可以保证每次构建用完全确定的策略执行不受Agent服务账号和环境变量的影响。注意CI里既然已经指定了-ExecutionPolicy就不要再在构建脚本里写“依赖执行策略”的逻辑否则换了环境又要踩一遍。6.4 Windows PowerShell 和 PowerShell 7 的策略不完全同步如果你装了PowerShell 7pwsh.exe它和经典Windows PowerShellpowershell.exe其实是两个独立的东西。虽然执行策略底层都存储在注册表里但PowerShell 7在部分Linux、macOS平台上根本不使用Windows的注册表策略而且在不同架构上加载策略的时机有差异。最保险的做法是两个环境里分别确认各自的Get-ExecutionPolicy值。别在一个环境里设置完跑到另一个环境里去验证很容易产生“我已经改了但为什么还报错”的错觉。同理如果你在某台机器上同时装了32位和64位PowerShell也要分环境确认。这也是为什么我建议在排查报错前第一步永远是当前环境下的Get-ExecutionPolicy而不是直接反问“你改过策略吗”。7. 关于执行策略最后想跟你说几句实在话从第一次被“禁止运行脚本”卡住到后来帮同事处理各种千奇百怪的PowerShell问题我最深刻的体会是这类问题很少是单一原因造成的多数时候是“执行策略没设对”和“MOTW没解除”两个因素叠加在一起有些还掺进PATH环境变量、32/64位程序差异、不同作用域优先级这些额外干扰。如果你现在正被这个问题困扰我建议按这个顺序做一遍基本能解决90%的现场问题先用Get-ExecutionPolicy -List看清楚所有层级再用Set-ExecutionPolicy RemoteSigned -Scope CurrentUser打开当前用户的基础权限接着用Unblock-File处理下载脚本的Internet标记最后如果涉及外部程序调用或CI流水线直接考虑启动参数-ExecutionPolicy Bypass -File不要动系统全局配置。另外别把执行策略当成一个非改不可的“障碍”。它本质上是系统在安全性和便利性之间给出的一个平衡点。你有意放开它、明确知道自己放开的是什么的时候这扇门可以帮你做更多事但如果只是一时嫌麻烦随手开成Unrestricted然后忘在脑后那才是真正埋了一颗雷。把这一套东西理顺之后PowerShell脚本跑起来真的只是个开始。后面你可能会慢慢接触模块化、自动化部署、配置管理这些更深层的玩法而执行策略这个基础问题解决了后面的路会顺很多。