ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows脚本实战指南:批处理与PowerShell自动化技巧全解

Windows脚本实战指南:批处理与PowerShell自动化技巧全解 Windows下面写脚本这事儿很多人觉得是运维或者开发才需要学的技能。但真到了工作中你会发现那些看似不起眼的.bat或.ps1文件能帮你把每天重复的点击、复制、清理、排查压缩成一条命令。我自己在Windows上攒了不少常用脚本从最基础的批处理到PowerShell自动化再到定时任务和故障排查踩过不少坑也总结了不少经验。今天就把这些内容系统地整理出来从一个实际使用者的角度聊聊怎么把Windows脚本用得顺手、用得放心。1. Windows脚本的两大主力批处理与PowerShell到底怎么选1.1 批处理.bat的适用范围与局限性批处理文件是Windows最早期的脚本形式基于CMD命令。它的核心优势是简单、直接、不需要额外运行时双击就能跑。只要是装了Windows的机器一定可以运行.bat文件。对于环境变量修改、文件复制、服务启停、网络命令这类偏“系统操作”的场景批处理的效率真的很高。但它也有很明显的短板。第一字符串处理能力弱写复杂逻辑很痛苦第二默认编码是系统本地编码中文容易乱码第三没有对象模型处理JSON、CSV、Excel这些结构化数据基本抓瞎。所以我的经验是如果任务只是几条系统命令的串联用bat如果任务涉及数据处理、交互、正则匹配果断选PowerShell。1.2 PowerShell才是复杂场景的首选PowerShell是微软在.NET基础上构建的脚本环境从Windows 7时代开始内置到Windows 10/11已经完全成为日常管理的核心。它最大的特点是“面向对象”命令输出不是纯文本而是带有属性的对象。比如查看进程普通命令行输出是一行文字PowerShell返回的则是带PID、进程名、内存占用、启动路径等属性的对象集合。这种差异意味着你可以直接在管道里Where-Object过滤、Select-Object投影、Export-Csv导出处理数据比文本正则匹配要稳得多。当然PowerShell也有一个让新手最头疼的坑默认执行策略是Restricted脚本直接被禁止运行。这不代表你没法用只是需要手动解除限制。注意执行策略不是说“这个是坏的”而是Windows的安全机制。别一上来就Set-ExecutionPolicy Unrestricted生产环境里建议用RemoteSigned只信任本地创建的脚本从网上下载的脚本会要求有数字签名避免半夜被恶意脚本搞挂服务器。1.3 快速判断什么场景用什么脚本我习惯用一个简单的表格来决策平时拿不准的时候翻一下就够了。任务类型推荐工具原因临时文件清理、开机启动项、计划任务批处理命令简单系统自带启动快端口排查与进程结束批处理netstat taskkill 组合很成熟文件批量重命名/移动批处理或PowerShell简单规则用bat复杂规则用PowerShell安全日志、事件日志分析PowerShellGet-WinEvent 直接返回结构化对象环境变量、服务状态监控PowerShell有专门Module可控性更强设备老化、压力测试等循环任务混合使用bat做循环框架PowerShell做日志采集选好工具脚本之旅就成功了一半。2. 日常高频场景的批处理脚本实战2.1 清理Windows临时文件的三行命令很多人的系统盘越来越小却不知道临时文件到底占了多大空间。Windows自带的“磁盘清理”其实已经很不错但如果你不想每次手动去开面板用批处理会更省心。下面这段脚本是我自己一直在用的包含用户临时目录、Windows更新缓存和回收站清理echo off rem 清理用户临时文件 del /q /s %TEMP%\* 2nul rem 清理Windows更新缓存 del /q /s %WINDIR%\SoftwareDistribution\Download\* 2nul rem 清理回收站 rd /s /q C:\$Recycle.Bin 2nul echo 清理完成按任意键退出。 pause这里有几个点要提醒你2nul是把错误信息重定向到空设备否则很多文件正被占用时会刷屏。清理Windows更新缓存时最好先停掉wuauserv服务Windows Update否则文件夹里的文件可能正在被系统使用。我一般在计划任务里放一个脚本先net stop wuauserv再清理最后net start wuauserv。不要试图清理C:\Windows\Temp以外的系统目录误删系统文件会造成启动故障。2.2 一键关闭占用端口告别“端口被占用”这是一个几乎每个Windows用户都会碰到的经典场景。你启动某个服务比如Elasticsearch、Tomcat结果提示端口被占用然后你就去“任务管理器”里一个个找进程看得头大。其实这一套操作完全可以写进脚本里。以端口8080为例echo off set PORT8080 for /f tokens5 %%a in (netstat -ano ^| findstr :%PORT%) do ( set PID%%a ) if defined PID ( echo 端口 %PORT% 被进程 PID%PID% 占用正在结束该进程... taskkill /pid %PID% /f ) else ( echo 端口 %PORT% 没有被占用。 ) pause解释一下逻辑netstat -ano会列出所有连接及对应PIDfindstr :%PORT%精确匹配端口行for /f tokens5提取第五列也就是PID。拿到PID后直接taskkill /pid /f强制结束。这个脚本的优化空间很大我后来会在开头加一层findstr过滤避免把列表中的其他端口也匹配进来。如果你要关闭的端口号有很多个也可以用for %%p in (8080 8081 8082)循环处理。注意taskkill /f是强制结束如果你正在跑的服务有未保存的数据可能会丢失。正式环境建议先taskkill /pid不带/f等3秒给进程优雅退出的机会。2.3 批量重命名文件名字不再一天天手动改批量重命名是我最开始学bat的动机。当时要给几百张图片改成“日期序号”的格式手动改到想骂人。后来一句for循环就解决了。echo off setlocal enabledelayedexpansion set count1 for %%f in (*.jpg) do ( ren %%f photo_!count!.jpg set /a count1 ) echo 完成共处理 %count% 张图片。 pause这里的关键是enabledelayedexpansion。因为count在循环里是动态变化的如果不开启延迟变量扩展!count!只会拿到循环开始前的值。这个坑我当年踩过写出来的文件全是photo_1覆盖了一堆照片幸好有备份。当然复杂一点的规则我建议用PowerShell比如去掉文件名中的特定字符、在名字中间插入日期PowerShell的-replace和Get-ChildItem配合起来更灵活。2.4 手动控制Windows更新脚本思路很多人对Windows自动更新又爱又恨爱的是安全补丁恨的是不定时重启打断工作。所谓“Windows Update Blocker”其实不必依赖第三方工具Windows自带的服务命令就能完成。net stop wuauserv sc config wuauserv start disabled echo Windows Update 服务已停止并禁用。开启则反过来sc config wuauserv start auto net start wuauserv echo Windows Update 服务已重新启用。要注意的是sc config需要管理员权限用bat运行时一定要右键“以管理员身份运行”。而且关闭Windows Update意味着你错过的安全更新只能通过手动补丁安装非长期维护的机器慎用。3. PowerShell进阶让脚本更聪明3.1 开机自启脚本的正确姿势“开机自启”并非只能往启动文件夹里塞快捷方式。更好的做法是使用任务计划程序这样你还能设置延迟启动、运行频率、失败重试等。命令行下用schtasks或者PowerShell的Register-ScheduledTask都可以。下面这个PowerShell脚本会在系统启动时以管理员身份运行一个指定的程序并延迟30秒启动$action New-ScheduledTaskAction -Execute C:\Tools\auto_backup.ps1 -Argument -WindowStyle Hidden $trigger New-ScheduledTaskTrigger -AtStartup $settings New-ScheduledTaskSettingsSet -Delay 30s -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries Register-ScheduledTask -TaskName StartupBackup -Action $action -Trigger $trigger -Settings $settings -RunLevel Highest这里有个重要细节-RunLevel Highest表示以最高权限运行很多脚本操作注册表、系统服务时没有这个参数就会失败。另外任务计划程序默认的触发条件是“仅当计算机使用交流电源时”在笔记本上如果没接电源开机启动的任务会被跳过这一点做自动化前要确认。3.2 环境变量问题为什么“pnpm无法识别”我在很多群里都看到过“pnpm : 无法将‘pnpm’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”的报错。这个场景非常典型也特别容易解释。它本质上是环境变量PATH里没有包含pnpm所在目录。你可以用PowerShell快速检查Get-Command pnpm -ErrorAction SilentlyContinue $env:PATH -split ; | Where-Object { $_ -like *pnpm* }如果没有输出说明pnpm的全局安装路径没有加入系统环境变量。正常情况下pnpm安装后会自动配置但有些安装方式比如npm全局安装不会自动写入。手动添加环境变量用PowerShell写会非常方便$newPath C:\Users\用户名\AppData\Roaming\npm $currentPath [Environment]::GetEnvironmentVariable(Path, User) [Environment]::SetEnvironmentVariable(Path, $currentPath ; $newPath, User) Write-Host 已将 $newPath 添加到用户环境变量。加完之后记得开一个新的终端窗口因为环境变量只在进程启动时读取。这个经验同样适用于git、Python、Java等一堆工具。3.3 实时监控安全日志与系统状态安全日志是Windows事件日志中最重要的部分记录了登录成功/失败、账户锁定、权限变更等。用PowerShell的Get-WinEvent可以很方便地分析最近的异常登录尝试。$since (Get-Date).AddDays(-7) Get-WinEvent -FilterHashtable {LogNameSecurity; ID4625; StartTime$since} -MaxEvents 20 | Select-Object TimeCreated, Id, {nameAccount; expression{$_.Properties[5].Value}}, {nameSourceIP; expression{$_.Properties[18].Value}} | Format-Table -AutoSize这个脚本会列出过去7天内所有登录失败Event ID 4625的事件并提取账户名和来源IP。主机上出现大量4625事件往往意味着有人在进行密码尝试。把脚本挂在计划任务里每天生成一份报告是非常现实的安全监控手段。不过需要提醒一点安全日志本身默认不一定记录成功/失败审核策略你需要先通过本地安全策略或组策略开启“审核登录事件”否则日志里是空的。这个用脚本同样可以处理auditpol /set /subcategory:登录事件 /failure:enable /success:enable3.4 用PowerShell处理文件编码与乱码写Windows脚本乱码问题绝对值得单独讲。默认情况下批处理文件用的是系统ANSI编码简体中文是GBK而PowerShell 5.x向下一旦文件保存为UTF-8里面如果带有中文运行时会显示乱码。这是因为Windows PowerShell默认按系统的活动代码页解析脚本文件。我建议统一使用UTF-8 with BOM编码保存PowerShell脚本这样Windows PowerShell能正确识别。如果你用Visual Studio Code写脚本在右下角选择“UTF-8 with BOM”即可。在脚本内部处理文本输出时可以显式指定编码Get-Content C:\data\raw.txt -Encoding UTF8 | Set-Content C:\data\out.txt -Encoding UTF8另外Out-File默认是UTF-16 LE如果你希望生成UTF-8要记得加-Encoding utf8。4. 把脚本变成“无人值守”定时任务与全自动执行4.1 用schtasks一分钟创建定时任务大多数人熟悉图形界面的任务计划程序但命令行才是脚本化的核心。schtasks可以从一个bat脚本中创建定时任务。比如每天凌晨2点执行备份脚本schtasks /create /tn DailyBackup /tr C:\Scripts\backup.bat /sc daily /st 02:00 /ru SYSTEM /rl HIGHEST参数含义/tn任务名/tr运行的程序或脚本路径/sc daily每天执行/st 02:00开始时间/ru SYSTEM使用SYSTEM账户运行避免用户未登录导致任务不执行/rl HIGHEST以最高权限运行创建完成后可以通过schtasks /query /tn DailyBackup查看状态。重点/ru SYSTEM配合SYSTEM账户很多运维同学忘记这个参数结果发现自己没登录桌面的时候任务根本不会启动。4.2 设备老化测试全自动执行脚本设计“设备老化测试全自动执行脚本”这个关键词在热搜里经常出现确实做硬件测试、长期稳定性测试的人会很关心。这种脚本的核心是“循环执行测试程序 记录日志 异常重启”。我分享一个我自己在跑的老化测试框架思路。整体逻辑是这样的主控脚本循环N次每次先清空旧日志再启动被测试的程序等待指定时间然后检测进程是否还在如果进程退出则记录错误最后写入测试日志。echo off set TEST_APPC:\Test\stress_test.exe set LOOP_COUNT100 set OUT_LOGC:\Test\aging_result.log for /l %%i in (1,1,%LOOP_COUNT%) do ( echo [%date% %time%] 第 %%i 轮测试开始 %OUT_LOG% %TEST_APP% %OUT_LOG% 21 if %errorlevel% equ 0 ( echo [%date% %time%] 第 %%i 轮测试通过 %OUT_LOG% ) else ( echo [%date% %time%] 第 %%i 轮测试失败errorlevel%errorlevel% %OUT_LOG% ) ) echo 老化测试全部完成。 %OUT_LOG%如果测试程序自己可能会让系统崩溃你还可以在计划任务中加入一个“Watchdog”脚本它会定时检查主控脚本是否还活着不活就强制重启机器schtasks /create /tn Watchdog /tr C:\Scripts\watchdog.bat /sc minute /mo 5 /ru SYSTEMwatchdog.bat 里判断某个标记文件是否更新超时则shutdown /r /f /t 0。这套组合能让老化测试真正达到“无人值守”的效果。4.3 定时任务失败后的通知机制自动化脚本最怕“跑了但没人知道结果”。我建议给关键任务加一个简单的邮件通知。PowerShell内置Send-MailMessage虽然在新版中被标记为弃用但依然可用。$smtp smtp.你的邮箱.com $to opsexample.com $from serverexample.com $subject 备份任务执行结果 $body 备份脚本执行完成请查看日志附件。 Send-MailMessage -SmtpServer $smtp -To $to -From $from -Subject $subject -Body $body -Attachments C:\Logs\backup.log生产环境我更推荐日志持久化到集中日志平台比如本地落盘 企业微信群机器人通知但家庭或小型项目里邮件已经足够。5. 脚本排障实录那些年我们闪退的脚本5.1 双击闪退的锅到底谁来背新手最容易碰到的就是写了个bat双击窗口一闪就没了什么也看不清。原因很简单脚本执行完CMD窗口自动关闭。解决办法是在脚本最后加pause这样窗口会停留让你看到输出结果。如果你在脚本中间需要看到更多调试信息建议先用命令行手动执行cmd /k C:\Scripts\test.bat/k参数表示执行完不退出直接留在CMD界面里。这个习惯能帮你解决90%的“闪退”问题。5.2 PowerShell执行策略的“坑”PowerShell脚本运行时报错十有八九是执行策略问题。系统默认Restricted也就是禁止运行任何脚本只允许命令交互。你需要在管理员PowerShell里执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUserRemoteSigned表示本地创建的脚本可以运行从网上下载到本机且未签名的脚本会被阻止。这个策略比Unrestricted安全得多也是微软推荐的开发环境配置。如果只想临时运行一个脚本而不想改全局策略可以powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\test.ps1这条命令对单次执行非常有效我经常用它来测试自己刚写的脚本。5.3 乱码、路径空格与管理员权限这三个问题看起来简单却是我帮别人排查时出现频率最高的。中文乱码脚本文件保存格式不对建议统一UTF-8 with BOM。路径有空格比如C:\Program Files\...必须用双引号包裹。在bat里C:\Program Files\App\test.exe。管理员权限很多命令sc、net stop、修改环境变量、写注册表需要管理员权限。PowerShell脚本里可以加一段提权代码if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] Administrator)) { Start-Process powershell.exe -Verb RunAs -ArgumentList $MyInvocation.MyCommand.Path exit }这段代码的意思是如果当前不是管理员权限就重新以管理员身份启动自身然后退出当前进程。5.4 细节决定成败错误处理与退出码脚本里明明每一步都该执行却总有一个地方悄悄跳过通常是因为没有检查错误。bat脚本可以通过errorlevel判断上一命令是否成功net stop SomeService if errorlevel 1 ( echo 服务停止失败异常退出。 exit /b 1 )PowerShell则用$?或$LASTEXITCODE。写脚本时一定要有意识地在关键操作后加判断否则自动化跑得越久错误积累越严重。调试的时候也别怕在脚本里多打几个echo或Write-Host把中间变量打印出来比什么高级调试器都直观。6. 脚本安全与合规别让快捷变成灾难6.1 为什么我从不下载“一键激活”脚本网上流传着各种“一键激活”“永久授权”“破解补丁”脚本看起来很诱人但这类脚本最大的风险是你根本不知道它会执行什么。轻则弹广告、改主页重则被植入木马、勒索病毒。我亲眼见过同事为了图省事下载一个所谓“激活脚本”结果整个办公网络被拖下水。脚本本身是文本文件没有任何可视化提示恶意代码完全可以混在一堆正常输出中静默执行。比如在pause之前插入一段powershell -Command ...下载远程恶意程序。所以我的原则是任何来源不明、作者不明的脚本不看内容绝不执行。6.2 自己写的脚本也要备份与审计脚本变得越来越常用了如果哪天误改了路径或逻辑可能会影响线上任务。我给自己定了个规矩所有脚本统一放在一个目录里用Git做版本管理。这样至少能追溯每次修改出问题时有后悔药。对于关键任务脚本我还建议在文件头加注释注明用途、依赖、最后修改人。维护一个几十行注释的脚本比维护一个什么都看不懂但能跑的脚本要轻松得多。日志审计同样重要。脚本里尽量记录执行时间、退出码、关键参数至少要在出错时能定位到是哪一行导致的问题。6.3 保持最小权限不要总用管理员跑很多脚本其实不需要管理员权限比如读取日志、统计文件大小。但很多同学习惯性地右键“以管理员身份运行”这会让脚本拥有系统最高权限一旦逻辑有漏洞影响面会扩大。我建议普通任务用普通用户身份运行只有真正需要修改系统设置的脚本如服务配置、注册表修改才提权。在计划任务中更是如此能用/ru SYSTEM之外的用户就用普通用户减少风险。6.4 一个小技巧把常用脚本整理成菜单最后分享一个我自己的习惯。我会把十来个常用脚本放到一个统一入口比如utils.bat打开后显示菜单选择数字执行对应功能。这样既方便日常使用也让团队里的人容易上手。echo off chcp 65001 nul :menu echo echo Windows 常用脚本工具箱 echo 1. 清理系统临时文件 echo 2. 关闭8080端口占用 echo 3. 关闭 Windows Update 服务 echo 4. 启动 Elasticsearch echo q. 退出 echo set /p choice请选择操作 if %choice%1 call :clean if %choice%2 call :killport if %choice%3 call :disablewu if %choice%4 call :start_es if %choice%q exit /b 0 goto menu把功能模块拆分成:label子例程主菜单只管分发维护起来特别清晰。一个小小的菜单脚本能让你的“常用脚本”从零散文件变成一套顺手用的工具链。写了这么多核心还是那句话Windows脚本是工具不是魔法。多积累、多用、多踩坑你的脚本库会越来越值钱。我每次新换电脑第一个迁移的东西就是那个装满脚本的文件夹因为它们几乎成了我工作效率的一部分。
返回列表