ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PowerShell高效进阶:从常用命令到自动化脚本的实战指南

PowerShell高效进阶:从常用命令到自动化脚本的实战指南 PowerShell这个东西Windows用户早晚都得碰一下。不管是排查“C盘怎么又满了”、启动一个Elasticsearch服务、批量给一堆电脑装环境还是单纯想把每天重复的复制粘贴工作省掉几遍PowerShell都是最顺手的那个入口。它不像老CMD那样只能跟纯文本打交道也不像Linux里的Bash那样在Windows上总有点水土不服——它是一整套基于对象的自动化平台自带一套叫“命令大全”都嫌小的命令体系。我写这篇博文就是想把这些年真正高频用到的PowerShell命令、脚本习惯、报错场景都梳理一遍争取让读者从“复制别人代码”慢慢进阶到“自己会查、会猜、会写”。有基础的朋友可以直接跳到后面看实战案例纯新手的建议从头顺着读每一步我都尽量带上“为什么这么做”的解释而不是丢一堆参数让你硬背。1. 先搞清楚PowerShell到底是什么为什么值得背它的命令1.1 从CMD到PowerShell一个更彻底的命令行世界很多Windows用户对PowerShell的第一印象就是“换了个名字的CMD”这种理解不能算全错但会严重低估它的价值。CMD做的事情是把操作系统的能力通过文本命令暴露出来输出结果是一堆字符串而PowerShell的核心思路是把一切操作对象化——文件是对象、进程是对象、服务也是对象。打个比方CMD的管道传的是“纸条”传过去之后你还得重新读一遍才知道上面写了什么PowerShell的管道传的是“带标签的箱子”每一项数据都有明确的类型和字段后面的命令打开箱子就能直接用。基于这个设计微软给PowerShell命令定了一套非常容易猜的命名规范动词-名词。Get表示读取Set表示修改New表示创建Remove表示删除Start和Stop表示启动和停止。你想看进程列表不用记奇怪缩写写Get-Process就行想停止某个进程把Get换成Stop就是Stop-Process。这套规则对不喜欢死记硬背的人非常友好遇到不认识的命令可以用Get-Command按“动词名词”的规律去猜猜完再用Get-Help验证一下基本八九不离十。1.2 什么人最需要这份命令清单PowerShell适合三类人。第一类是开发者和运维日常要在本地启动Elasticsearch、Redis、Docker这类服务或者连上服务器做日志检索、端口排查、服务启停PowerShell就是最顺手的操作台。第二类是系统管理员要做批量部署、开机自启、安全日志审计、计划任务脚本化之后效率翻倍。第三类是普通Windows用户系统卡了想看哪个进程占内存C盘满了想找出大文件弹窗广告想删掉开机启动项这些都可以在PowerShell里用几行命令搞定。另外多说一句现在很多运行在Windows上的第三方工具装完后你仍然要在命令行里敲它们——Git、Docker、Node、Python、conda、vim全都一样。PowerShell作为宿主几乎是Windows下所有命令行工具的“通用入口”。哪怕你只写前端只做数据分析也躲不开要跟这个黑窗口打交道。2. 打开PowerShell与版本选择别在入口就卡住2.1 快速打开PowerShell的多种姿势经常有人问我“PowerShell怎么打开”这问题听起来很简单但确实有人在入口就卡住。我平时最常用的几种方式按效率排个序按WinX在弹出的菜单里选“Windows PowerShell”或“终端”。按WinR输入powershell回车。在资源管理器地址栏直接输入powershell回车——这个方式有个隐藏优势打开后当前目录就是你正在看的文件夹省得再费力切路径。开始菜单直接搜“powershell”或“pwsh”。这里有个细节特别强调一下如果接下来要操作磁盘、服务、系统文件建议右键选择“以管理员身份运行”。我见过很多次“访问被拒绝”的报错根本不是命令写错而是普通权限窗口没资格动系统级资源。判断方法很简单看窗口标题标题里有“管理员”三个字说明是提权状态。原理上PowerShell继承当前进程的用户访问令牌普通权限的令牌即便在命令里加-Force也没有用操作系统访问控制层会直接拒绝。2.2 版本怎么选5.1还是7.xWindows里至少有“两个长得不一样”的PowerShell。系统自带的是Windows PowerShell 5.1它基于.NET Framework和Windows系统组件深度绑定老脚本兼容性最好另一个是PowerShell 7.x基于.NET支持跨平台启动更快、字符处理更现代但和系统组件的耦合度低。两个版本完全可以共存平时执行老脚本用5.1自己写新东西我用7.x。在命令行里输入powershell打开的是5.1输入pwsh打开的才是7.x别搞混。先看自己现在是什么版本直接在PowerShell里执行$PSVersionTable返回结果里有个PSVersion字段就是当前版本号。升级PowerShell 7最简单的方式是winget install Microsoft.PowerShell对5.1一般不用主动升级系统更新会带着补丁一起打。如果因为特殊原因必须装5.1Win7这类老系统对补丁依赖比较强装之前最好把.NET Framework和系统更新补齐安装包从微软官方渠道下载不要信第三方“一键安装”。2.3 执行策略第一个“无法加载”警告的真相新用户第一次运行.ps1脚本经常会看到一个红字提示无法加载文件因为在此系统上禁止运行脚本。这不是脚本坏了是执行策略在保护你。默认情况下Windows PowerShell的ExecutionPolicy是Restricted不允许本机运行任何脚本。微软设计这套机制是为了防止恶意脚本在用户不知情的情况下被双击执行。常见策略有Restricted、AllSigned、RemoteSigned、Unrestricted、Bypass几档。对个人开发机我推荐RemoteSigned本地自己写的脚本可以运行从网络下载的脚本要求有数字签名。设置命令是Set-ExecutionPolicy RemoteSigned需要管理员权限。还有一种临时放行方式powershell -ExecutionPolicy Bypass -File xxx.ps1只对当次命令生效不改变系统设置。这种用法比较克制适合你明确知道脚本内容可信、但不想全局放开策略的场景。反过来也要提醒一句平时看到“从网络下载脚本然后直接执行”的做法要谨慎脚本在你机器上拥有和你一样的权限别人写好的脚本先看清楚再跑。3. 高频命令实战文件、进程、网络与服务3.1 文件和目录操作先学会用管道筛选文件目录操作是最高频的场景先掌握几个最基本的。切换目录用Set-Location也可以直接用别名cd查看当前位置用Get-Location别名pwd列出内容用Get-ChildItem别名ls或dir。CMD习惯的玩家到这里都不会陌生。关键在参数-Recurse表示递归子目录-Filter用来做过滤-ErrorAction SilentlyContinue让命令遇到没权限的目录时不要刷屏报错、直接跳过。一个非常实用的场景找出C盘里大于1GB的文件。一条命令就能搞定Get-ChildItem -Path C:\ -Recurse -File -ErrorAction SilentlyContinue | Where-Object Length -gt 1GB | Sort-Object Length -Descending | Select-Object FullName, {Name大小GB; Expression{[math]::Round($_.Length/1GB,2)}}这段就是“先列所有文件再筛选大于1GB的按大小倒序排最后只输出路径和换算后的大小”。如果不想看C盘全盘可以把-Path C:\改成具体目录速度会快很多。复制、移动、删除的命令分别是Copy-Item、Move-Item、Remove-Item配合-Recurse和-Force使用。Remove-Item的-Force参数很危险删文件时它会绕过只读属性删目录时报错也少稍不注意就会把不该删的东西删掉。所以行动前务必确认路径正确必要的时候先在末尾加上-WhatIf参数预演一遍看看命令到底会动哪些文件。3.2 进程与服务排查系统卡顿自救指南系统卡顿的时候PowerShell是很好的体检工具。先看谁在吃CPUGet-Process | Sort-Object CPU -Descending | Select-Object -First 10输出的前几行基本就是罪魁祸首。确认某个进程要结束后用Stop-Process -Name notepad -Force这样的命令直接终止。这里提醒一下-Force对Stop-Process来说是“强制结束”有些程序来不及保存数据所以我一般先不用-Force杀不掉再加。服务排查比进程更常用。Get-Service可以列出所有服务想看哪些在运行Get-Service | Where-Object Status -eq Running重启打印机服务、重启IIS服务这类操作一条Restart-Service -Name spooler就完成了。比较麻烦的是“进程已经看到了但不知道它属于哪个服务”的情况这时候用Win32_Service类查询Get-CimInstance Win32_Service | Where-Object ProcessId -eq 12345把12345换成进程ID就能反查进程ID对应的服务名、显示名、启动类型。排查服务器异常时这一招非常管用。如果想看安全日志里有没有大量失败的登录尝试可以用Get-WinEvent -FilterHashtable {LogNameSecurity; Id4625} -MaxEvents 20输出里能看到最近20条登录失败记录包括来源IP和用户名。系统管理员审计账户安全时这条命令比在事件查看器里点来点去快得多。3.3 网络诊断一条命令替代telnet测端口早年间排查网络最常用的命令是telnet ip 端口用来测试某台机器的某个端口通不通。但Windows 10/11默认不装telnet客户端装起来还麻烦而且telnet一旦连上就是黑屏等待用户体验很差。我后来基本只用一条命令替代Test-NetConnection -ComputerName 192.168.1.10 -Port 3389返回结果里看TcpTestSucceeded字段True代表端口通False代表不通。这个命令本质上是把“ping测试DNS解析TCP端口测试路由追踪”都做了一遍加个-TraceRoute参数还能顺便看经过的路由节点。查本机端口占用也是高频场景。比如启动服务时提示8080端口被占用先看谁在监听Get-NetTCPConnection -State Listen Get-NetTCPConnection -LocalPort 8080拿到OwningProcess字段的PID后再用Get-Process -Id 8080的PID去确认是哪个程序。这一套组合拳查端口冲突特别顺手。再配合日常服务排查说一下在Windows上启动Elasticsearch启动完通常要确认9200端口是否起来了直接Test-NetConnection -ComputerName localhost -Port 9200返回True就说明服务就绪。Redis默认6379Docker映射的端口也可以这样验证。网络命令不用背太多把Test-NetConnection、Get-NetTCPConnection、Resolve-DnsName这三个用熟大部分问题都能解决。3.4 在PowerShell里调用外部命令git、docker、服务启动都是它的活儿单论文件、进程、网络这些都是PowerShell自家的命令但实际工作里更常出现的是“在PowerShell里敲别人家的程序”。Git、Docker、Node、conda、vim装好之后都会被加进PATH直接敲名字就能跑git status、docker ps、python --versionPowerShell不会拦你。有几个小坑值得记一下。第一如果程序路径里有空格直接敲路径会报“不是内部或外部命令”这时候用调用操作符 C:\Program Files\MyTool\tool.exe -param。第二想把一个目录临时加入当前会话的PATH执行$env:Path ;C:\tools只对当前窗口生效关掉就没了想持久生效还是得去“系统属性-环境变量”里改。第三PowerShell里有几个命令别名和外部工具重名最经典的是curl在PowerShell里curl默认是Invoke-WebRequest的别名行为完全不同真要调用原生命令请写curl.exewget同理。我在Windows上启动Elasticsearch的习惯步骤是解压后切到bin目录执行.\elasticsearch.bat然后另开一个窗口用Test-NetConnection localhost -Port 9200验证是否就绪。启动Redis、启动Docker容器、查看容器日志也都在同一个PowerShell窗口里完成。所以说先把PowerShell本身用顺Windows上几乎所有命令行工具都能统一管理起来。4. 脚本与自动化让PowerShell替你做重复劳动4.1 反手一个C盘清理脚本C盘清理是永恒话题。手动删临时文件太累用图形界面里的磁盘清理又不够彻底。我习惯写一个PowerShell脚本定期清一遍。先看空间占用Get-PSDrive C然后清理临时目录、回收站并统计释放了多少空间。完整脚本如下# Save as Clean-C.ps1管理员运行 $before (Get-PSDrive C).Used $paths ( $env:TEMP, C:\Windows\Temp, $env:LOCALAPPDATA\Temp ) foreach ($dir in $paths) { Get-ChildItem -Path $dir -Recurse -Force -ErrorAction SilentlyContinue | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } Clear-RecycleBin -DriveLetter C -Force -ErrorAction SilentlyContinue $after (Get-PSDrive C).Used $freed ($before - $after) / 1GB 清理完成释放约 {0:N2} GB 空间 -f $freed这段脚本做了三件事读取清理前C盘已用空间遍历三个临时目录删除文件清空回收站最后输出释放空间。关键点在$env:TEMP是当前用户临时目录的变量C:\Windows\Temp是系统临时目录清理后者需要管理员权限。每个删除命令都加了-ErrorAction SilentlyContinue因为部分文件正被系统占用、删不掉很正常没必要让脚本中断。第一次跑之前建议改一下把Remove-Item换成带-WhatIf的形式先看预演结果确认它只会删临时目录里的内容再真正执行。PowerShell脚本是纯文本保存为.ps1文件后在PowerShell窗口里输入.\Clean-C.ps1就能运行。4.2 开机自启与定时任务怎么挂很多人做“开机自启”第一反应是往启动文件夹里放快捷方式但对PowerShell脚本或者后台任务来说更好的方案有几个。先看最简单的方法按WinR输入shell:startup打开启动文件夹在里面放一个快捷方式目标设置为powershell.exe -WindowStyle Hidden -File C:\Scripts\Clean-C.ps1这样开机登录后脚本会在隐藏窗口里执行不会弹个黑框吓人。如果希望更可控用计划任务。比如每天上午9点自动清理C盘可以用PowerShell直接注册$action New-ScheduledTaskAction -Execute powershell.exe -Argument -WindowStyle Hidden -File C:\Scripts\Clean-C.ps1 $trigger New-ScheduledTaskTrigger -Daily -At 09:00 Register-ScheduledTask -TaskName DailyCleanup -Action $action -Trigger $trigger -Force计划任务比启动文件夹强的地方在于可以指定触发时间、错开启动高峰、按天或按周重复。注意“不管用户是否登录都要运行”这个选项需要存账户密码自己机器上不想折腾就选“只在用户登录时运行”。还有老派做法往注册表Run键里写启动项。命令是New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Run -Name MyScript -Value powershell.exe -File C:\Scripts\Clean-C.ps1。这方法简单但问题是不好管理开机启动过多还会拖慢登录速度所以我的建议是简单自启用启动文件夹要定时、要条件就用计划任务。4.3 批量操作把“命令大全”变成“一条管道”PowerShell最迷人的地方是管道和批量操作。正常你写10条命令做的事情用管道往往1条搞定。举几个例子。批量重启一组服务Spooler,WSearch | Restart-Service -ErrorAction SilentlyContinue字符串数组通过管道一个一个传给Restart-Service每条命令都作用于一个服务。这是最简单的批量。扫描一个IP段里开放了哪些端口1..20 | ForEach-Object { $r Test-NetConnection -ComputerName 192.168.1.1 -Port $_ -WarningAction SilentlyContinue if ($r.TcpTestSucceeded) { Port $_ open } }这里用1..20生成了1到20的数字管道ForEach-Object每次取一个数字放进$_然后逐个做端口测试。批量扫描内网主机或验证服务端口时这个模式非常实用。批量改文件名也比较常见。比如把当前目录所有.txt文件改成.mdGet-ChildItem *.txt | ForEach-Object { Rename-Item $_.FullName -NewName ($_.BaseName .md) }管道传过来的是文件对象所以$_有FullName、BaseName、Extension这些属性直接拿来加工成新名字。这种写法比写for循环处理字符串再拼接路径要直观得多。我的体会是PowerShell的管道不要当成过场它是整个脚本体系的灵魂。命令输出的对象带着属性下一步命令可以直接引用省掉大量解析文本的时间。5. 高频报错与排查实录踩过坑才不会慌5.1 安装和升级报错错误代码 -2146869246 怎么处理遇到“安装程序无法安装 Windows PowerShell。错误代码为 -2146869246”的朋友不少。这个错误代码本质上是Windows Installer层的错误常见原因有几类系统更新不完整、.NET组件损坏、或者旧版本的PowerShell组件没有清理干净。我的排查顺序一般是这样的先把系统更新打全。装新版PowerShell或升级5.1之前确认.NET Framework 4.x已经装好。用管理员身份打开PowerShell依次执行DISM和SFC修复系统文件DISM /Online /Cleanup-Image /RestoreHealth sfc /scannow这两个命令会把系统映像里的损坏文件修一遍时间有点长中途别关窗口。临时退出安全软件后重试安装装完再把安全软件开回来。安装包务必从微软官方渠道下载。PowerShell 7优先用winget install或Microsoft Store不要用第三方搬运的精简版。还不行就先把旧的PowerShell相关组件卸载掉重启系统再装。Win7这类老系统装5.1之前补丁需求更明显建议先把系统更新打到最新。排查这个问题时很多人会漏掉“DISM修复”这一步其实它才是解决安装类错误最通用的手段。5.2 乱码问题中文显示成“锟斤拷”的真相PowerShell中文乱码是另一大经典问题。大部分场景是控制台代码页不一致造成的。Windows PowerShell 5.1默认读取和输出文本时会按系统ANSI编码来简体中文环境就是GBK而很多脚本文件、程序输出是UTF-8两边对不上中文就变成一堆乱码。解决办法分几层。先临时切换代码页chcp 65001把控制台切到UTF-8代码页。如果只是当前窗口有效重开窗口就失效。想在脚本里固定输出编码可以设置控制台编码[Console]::OutputEncoding [System.Text.Encoding]::UTF8更隐蔽的坑在脚本文件的保存格式。Windows PowerShell 5.1对“无BOM的UTF-8”文件识别不好用记事本或VS Code写脚本时一定要把.ps1文件另存为“UTF-8 with BOM”格式PowerShell 7.x对编码处理更现代这个问题小很多。如果你只是运行某个具体程序时看到乱码比如Python输出中文乱码先检查程序的输出编码设置再回头看终端自身的编码。不要一上来就去改“系统区域设置里的Beta使用Unicode UTF-8提供全球语言支持”那个选项虽然能全局解决乱码但可能让一部分老程序显示异常谨慎使用。5.3 其他高频报错速查表把平时群里问得多的报错整理成一张速查表方便收藏报错现象常见原因处理思路无法加载文件因为在此系统上禁止运行脚本ExecutionPolicy限制管理员执行Set-ExecutionPolicy RemoteSignedxxx不是内部或外部命令命令不存在或未加入PATH用Get-Command检查配置环境变量拒绝访问权限不足用管理员身份重开PowerShellgpedit.msc打不开系统版本不含组策略编辑器检查系统版本家庭版建议用注册表替代中文乱码控制台代码页不一致或脚本编码问题chcp 65001 脚本另存为UTF-8 with BOM找不到模块或命令模块未安装或未导入Install-Module / Import-Module端口被占用进程监听该端口Get-NetTCPConnection定位PID后结束进程脚本执行到一半卡住命令在等待输入或网络超时CtrlC中断查看报错定位表格里最常被忽略的是“脚本执行到一半卡住”这一条。很多PowerShell新手以为脚本死循环了其实是某个命令在等待确认或等待网络响应。比如Remove-Item删只读文件时不加-Force会弹确认Test-NetConnection连一个不通的IP会等很久。遇到卡住先按CtrlC再检查是不是少了参数。补充一个“命令不存在”的排查技巧报错之后先执行Get-Command xxx如果返回空说明命令真的不在当前环境里如果返回了某个程序路径但运行还是报错多半是路径或参数问题。Windows自带的gpedit.msc打不开常见原因是家庭版系统没有组策略编辑器这种需求可以考虑用注册表替代或者查微软官方文档确认该功能支持的版本。最后再分享一个心态上的建议PowerShell不是靠背命令学会的是靠“猜命令查帮助跑一遍”学会的。常用命令就那几十条剩下的大可以用Get-Help现查。执行破坏性操作前多花10秒检查路径、多用-WhatIf预演大多数坑都是可以避开的。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进