你有没有过这样的经历:每天上班第一件事,打开命令行敲一串重复的命令关端口、清临时文件、重启服务;明明只是改个文件名,却一个个右键重命名;电脑用着用着就卡,打开任务管理器发现一堆不明进程占着端口。这些问题,Windows下都有对应的脚本可以处理。我自己从最早只会写.bat复制文件,到后来用PowerShell把日常工作里80%的重复操作都装进了脚本,这几年攒下来一批真正常用的Windows脚本,今天整理出来,顺便把那些踩过的坑也一并说清楚。如果你用的也是Windows,不管是办公、开发还是运维场景,这篇内容应该能帮你省下不少时间。
1. 为什么我建议每个Windows用户都建一个脚本文件夹
1.1 脚本能替你干的四类重复劳动
先说结论:Windows脚本并不是只有运维才用得上。日常办公里,把一个几千行的Excel导出成CSV、把一批照片按日期重命名、给每个子目录生成一份清单,这些活儿用PowerShell都是几行命令的事。我自己把这些需求分成了四类:
- 信息收集类:查端口占用、查进程、导系统日志、查硬件配置,脚本跑一遍比对着界面点半天快得多;
- 批量处理类:重命名、批量创建文件夹、批量改文件编码、批量压缩解压;
- 定时任务类:开机自启、每天清理临时文件、定期备份数据库、定时导出报表;
- 环境修复类:端口被占、DNS缓存异常、网络重置、服务卡死,一键脚本直接恢复。
你如果细想一下,这些事单独操作可能也就几分钟,但只要变成日常习惯,累积起来非常可观。我之前给一批办公设备做老化测试,需要连续跑十几个脚本采集温度、内存、磁盘状态,手工操作根本没戏,最后就是用计划任务串起来的。Windows脚本的真正价值不是“省一次操作”,而是把你脑子里那套重复流程固化下来,让机器按你的规则去跑。
信息收集类脚本最容易上手,也最容易带来成就感。你只需要把平时会手动输入的几条命令拼在一个文件里,以后双击一下就能看到结果。批量处理类稍微需要一点逻辑思维,核心就是“遍历一批对象,然后对每个对象执行同一个动作”。定时任务类和环境修复类则更接近“自动化运维”的概念,等脚本库攒到一定数量,你会有一种“电脑在替我打工”的感觉。
1.2 脚本文件夹的分类与命名规范
我建议所有人从今天开始做一件事:在D盘建一个Scripts文件夹,并按用途分子目录。我自己是这样组织的:
D:\Scripts ├── 01_net # 网络相关 ├── 02_sys # 系统维护 ├── 03_dev # 开发辅助 ├── 04_data # 数据处理 └── README.md # 记录每个脚本用途命名上我习惯用“动词+对象”的方式,比如kill_port.ps1、clean_temp.ps1、export_log.ps1。别小看这个习惯,脚本一多,名字没起好,三个月后你自己都想不起来哪个是干嘛的。README里不止要写用途,还要写运行前提,比如“需要管理员权限”、“依赖Python环境”这种坑。
还有一个很实用的小动作:把这个Scripts目录加入用户环境变量的Path里。具体操作是:右键“此电脑” -> 属性 -> 高级系统设置 -> 环境变量 -> 在用户变量里找到Path,点编辑 -> 新建,把D:\Scripts加进去。保存后重开一个PowerShell窗口,你就可以在任何目录下直接输入脚本名运行,不用每次都输完整路径。这个操作值得在每台新电脑上重复一遍,基本是一劳永逸。
2. 写脚本之前:先搞懂cmd、PowerShell和运行策略这三件事
2.1 cmd的批处理和PowerShell,到底选哪个
经常有人纠结写.bat还是.ps1。我的判断标准很简单:只做两三步固定操作,比如复制文件、启动程序、把批处理工具串起来,.bat足够;一旦涉及过滤、循环、格式处理、调用系统API,就果断选PowerShell。
原因在于PowerShell处理的是对象而不是文本。举个例子,你想找出占用8080端口的进程,在cmd里你得netstat -ano | findstr :8080,拿到一串文本再自己找PID;而PowerShell能一行拿到进程对象,直接结束它。另一个差异是语法。cmd里处理变量用%var%,稍不注意就出幺蛾子;PowerShell用$var,语义直白。所以新写的脚本我几乎都推荐PowerShell。
我整理了一个简单的对比,供你选型时参考:
| 对比项 | cmd批处理(.bat) | PowerShell(.ps1) |
|---|---|---|
| 适合场景 | 简单固定命令的串联 | 逻辑判断、循环、对象处理 |
| 变量写法 | %var% | $var |
| 输出 | 文本流 | 对象流,可管道处理 |
| 错误处理 | 很弱,基本靠错误级别码 | try/catch,异常对象 |
| 学习曲线 | 平缓但上限低 | 需要了解对象概念 |
我的建议是:老脚本可以继续用bat兼容,但新写的脚本一律考虑PowerShell。不要觉得PowerShell命令长,平时多用缩写和别名,顺手之后效率反而更高。比如Get-ChildItem可以简写成dir,Get-Process可以简写成ps,既不改变表达能力,输入又短。
2.2 执行策略:为什么你写的.ps1双击没反应
PowerShell刚装好的时候,默认执行策略是Restricted,意思是任何.ps1脚本都不允许执行。你双击一个.ps1文件,系统通常会直接用记事本打开,或者弹个窗口一闪而过,不是脚本写错了,是根本不让跑。
解决办法是修改执行策略。我建议用RemoteSigned,它允许本地创建的脚本运行,从网上下载的脚本需要签名。在PowerShell里执行:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser注意作用域写CurrentUser,不要去改MachinePolicy或LocalMachine,一是避免影响系统全局,二是没有管理员权限时改不动。改完之后可以用Get-ExecutionPolicy -List确认各级策略的最终生效值。执行策略理解成给脚本发“通行证”,远程脚本一律先扣下检查,本地脚本放行,这个策略日常够用。
如果要深究,PowerShell执行策略有Restricted、AllSigned、RemoteSigned、Unrestricted、Bypass等几档。AllSigned要求所有脚本都有数字签名,个人写脚本几乎不会用。Bypass相当于完全不管,适合写临时命令但不建议设为默认。RemoteSigned是安全性和便利性的平衡点。你还会看到组策略里可能有“机器策略”和“用户策略”两层,以本地设置为准时,CurrentUser是最不容易出问题的作用域。
2.3 编码和闪退:新手最容易卡住的两个问题
这部分要专门说,因为“windows脚本命令闪退”真的是高频搜索词。最常见的场景是双击一个.bat文件,窗口一闪就没了,你想看错误内容都来不及。不是脚本崩溃,是批处理窗口执行完默认就自动关闭。临时解决办法是在文件最后加一行pause,或者在cmd里执行cmd /k 你的脚本.bat,窗口会保留。我更推荐正式调试时直接用PowerShell窗口运行,这样错误信息能看得更清楚。
中文乱码也常见。用记事本新建的.ps1文件,默认是ANSI编码,在Windows PowerShell 5.1里中文经常变成乱码。解决办法是把文件保存成“UTF-8 with BOM”格式。VS Code右下角就能改编码。如果用的是PowerShell 7,默认UTF-8处理就没这么麻烦。所以强烈建议装一个PowerShell 7,既保留兼容性,又省掉一堆编码坑。
还有一个容易被忽略的问题:脚本文件第一行的注释,如果包含全角字符或者中文引号,在某些版本的编辑器里会造成解析错乱。我的习惯是第一行只写简单的英文注释,或者干脆不写,把中文说明放到脚本末尾。这个技巧虽然听起来很土,但确实帮我省过很多排查时间。
3. 高频场景:8个我一直在用的Windows脚本实例
3.1 一键干掉占用端口的进程
日常开发最烦的一句话就是“端口被占用”。以前我的操作是:netstat -ano找PID,再用tasklist看是哪个程序,最后taskkill结束它。三步操作重复了无数遍。后来写成脚本,只需输入一个端口号。
param( [int]$Port = 8080 ) $conn = netstat -ano | Select-String ":$Port\s" if ($conn) { foreach ($line in $conn) { $parts = ($line.Line.Trim() -split '\s+') $pidToKill = $parts[-1] Write-Host "端口 $Port 被 PID $pidToKill 占用,正在结束进程..." taskkill /PID $pidToKill /F } } else { Write-Host "端口 $Port 没有被占用。" }使用前记得用管理员身份运行PowerShell,否则taskkill会提示拒绝访问。脚本里我加了默认值8080,直接运行就查8080,带参数就查别的端口,比如.\kill_port.ps1 -Port 3306。这类脚本的痛点是netstat输出格式不一定一样,偶尔会出现空行,所以代码里要先Trim再split,防止取到空白PID。
3.2 导出最近一天的Windows安全日志
排查安全问题或者设备异常登录时,安全日志非常关键。图形界面里看事件查看器不是不行,但想导出当日日志再筛选,费时费力。我用PowerShell一行搞定:
$events = Get-WinEvent -FilterHashtable @{LogName='Security'; StartTime=(Get-Date).AddDays(-1)} $events | Select-Object TimeCreated, Id, LevelDisplayName, Message | Export-Csv -Path "D:\Logs\Security_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation执行前先创建D:\Logs目录。安全日志默认只有管理员能读,所以这个脚本必须提权运行。如果只想关注登录失败,可以加条件Where-Object {$_.Id -eq 4625},4625就是登录失败的事件ID。导出CSV之后,直接丢进Excel筛选,比在事件查看器里翻页舒服多了。我一般还会顺手把4624(登录成功)也导出来,这样对比起来能看到完整的登录时间线。
3.3 批量重命名文件,三秒钟搞定几百个文件
给一批照片或者报表加日期前缀,或者在中间插入项目编号,手工操作真的会崩溃。我用的是下面这个版本:
$files = Get-ChildItem "D:\Photos\*.jpg" $date = Get-Date -Format "yyyyMMdd" foreach ($file in $files) { $newName = "{0}_{1}{2}" -f $date, $file.BaseName, $file.Extension Rename-Item -Path $file.FullName -NewName $newName -WhatIf }注意我留了一个-WhatIf参数,这是PowerShell里非常实用的“演习开关”。加上它之后,脚本只显示“将要执行什么”,不会真的改文件名。第一次跑任何批量修改类脚本,都建议先开着-WhatIf检查一遍输出,确认无误再删掉重跑。不要觉得多此一举,批量操作一旦出错就是几百个文件遭殃。
如果你需要更复杂的重命名规则,比如把文件名里的“2023”改成“2024”,可以在循环里用$file.Name.Replace(...)处理文本,然后再拼接新名称。只要规则明确,PowerShell的表达能力足够覆盖常见需求。
3.4 一键清理系统临时文件
每次清理磁盘垃圾,我都是先Win+R输入%temp%,然后进去Ctrl+A删除,删到一半又提示“文件正在使用”。现在直接用脚本,跳过所有正在占用的文件:
$tempPaths = @( "$env:TEMP", "C:\Windows\Temp" ) foreach ($path in $tempPaths) { Get-ChildItem -Path $path -Force -ErrorAction SilentlyContinue | Where-Object { -not $_.PSIsContainer } | Remove-Item -Force -ErrorAction SilentlyContinue } Write-Host "临时文件清理完成。"这个脚本只删文件不删目录,避免把某些软件的临时目录结构误删。-ErrorAction SilentlyContinue的作用是遇到被占用或者权限不足的文件时跳过,而不是中断整个清理流程。建议配合计划任务,每个星期自动跑一次,磁盘清爽很多。
如果想进一步清理,可以把C:\Users\用户名\AppData\Local\Temp也加进路径列表。不过别碰C:\Windows\WinSxS,那个目录是Windows组件存储,用脚本乱删会导致系统更新出问题。清理脚本的原则是“只碰明确安全的临时目录”。
3.5 开机自启:把自己的工作环境一键拉起
热词里有个“powershell开机自启脚本”,说明很多人想实现开机自动执行。最不推荐的做法是把脚本的快捷方式直接塞进启动文件夹,因为会弹出一个黑窗口,放在桌面上很烦。我通常用下面这个脚本,在启动文件夹里创建一条指向PowerShell的快捷方式,并隐藏窗口:
$startupFolder = [Environment]::GetFolderPath('Startup') $shortcutPath = Join-Path $startupFolder "start_work.lnk" $targetPath = "D:\Scripts\start_work.ps1" $shell = New-Object -ComObject WScript.Shell $shortcut = $shell.CreateShortcut($shortcutPath) $shortcut.TargetPath = "powershell.exe" $shortcut.Arguments = "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$targetPath`"" $shortcut.Save()这样每次登录系统,PowerShell会在后台静默执行start_work.ps1,没有窗口弹出来。要注意-ExecutionPolicy Bypass只针对这次调用,不会修改系统策略。另外,自启脚本里不要放需要管理员权限的操作,因为登录时默认权限不够,很多命令会静默失败。如果确实需要管理员权限,可以放到计划任务里,用“登录时”触发器并勾选“使用最高权限运行”。
3.6 在Windows下用脚本批量执行MySQL的SQL文件
做开发的人经常需要初始化数据库。图形客户端虽然支持导入SQL,但一次导多个文件很痛苦。cmd脚本里直接这样写:
@echo off mysql -u root -p -D testdb < D:\scripts\init.sql这条命令会提示输入密码,输入后自动执行SQL文件。好处是密码不留在脚本里,比较安全。如果要做无人值守执行,建议用--defaults-extra-file参数指向一个单独保存连接信息的配置文件,把密码放在那个文件里,而不是直接写在命令行上。命令行里带密码,用tasklist /v或历史记录都可能泄露,这个习惯一定要养成。
配置文件my.cnf内容大致是这样:
[client] user=root password=你的密码 host=127.0.0.1执行时写mysql --defaults-extra-file=D:\scripts\my.cnf -D testdb < D:\scripts\init.sql。同时给my.cnf设置一个仅当前用户可读的权限,而不是放在共享目录里。数据库密码属于高价值机密,脚本里的“明文密码”问题值得所有开发者警惕。
3.7 使用Windows自带命令下载文件
很多教程让你装第三方下载工具,实际上Windows 10和Windows 11自带的curl.exe就够了。基本用法:
curl.exe -L -o D:\Downloads\file.zip "https://example.com/file.zip"这里有个高频坑:PowerShell里直接敲curl会调用Invoke-WebRequest的别名,而不是真正的curl,导致参数不兼容。必须写curl.exe,把扩展名写全,才能调用系统里的curl程序。想下载复杂资源,比如需要设置请求头、带Cookie,curl参数会更丰富。这条命令用来拉公共安装包、脚本文件,特别方便。
如果你的PowerShell版本很老,没有curl.exe,可以用Invoke-WebRequest -Uri ... -OutFile ...,但速度通常不如curl快。下载大文件、断点续传这类场景,建议直接配合-C -参数使用curl的断点续传能力,比图形浏览器省心。
3.8 快速收集系统信息,设备体检
这个脚本适合给新电脑做体检,也适合处理“设备老化测试自动执行”这类场景。下面几行命令能一次拿到系统版本、内存总量和磁盘剩余空间:
Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, TotalVisibleMemorySize Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select-Object DeviceID, @{N='FreeGB'; E={[math]::Round($_.FreeSpace/1GB, 2)}}第一条里的内存单位是KB,要转成GB就除以1MB(PowerShell里1MB是1024KB)。第二条我只筛选了本地磁盘。这个脚本可以配合计划任务,把输出重定向到日志文件,长期收集设备状态,为老化测试提供数据。设备状态采集脚本的通用套路就是:先Get-CimInstance获取信息,再把结果追加到CSV,最后用Export-Csv -Append续写。
4. 脚本写好了却跑不起来:闪退、被拦截、路径空格三大坑
4.1 闪退的完整排查链路
先说一句扎心的:脚本闪退90%不是脚本本身错了,是你还没看到错误信息就关窗口。排查链路由下往上走:
- 先在PowerShell窗口里手动执行脚本,比如
.\script.ps1,这时所有错误都停留在屏幕上; - 如果是bat文件,用
cmd /k script.bat保留窗口; - 还是没有输出,检查脚本文件编码,UTF-8 with BOM能避免中文乱码导致语法解析出错;
- 再看脚本第一行是否写错了,PowerShell脚本里有中文引号或者全角符号,经常直接报错;
- 最后查执行策略,
Get-ExecutionPolicy如果不是RemoteSigned或Unrestricted,执行就会被拦。
这个链路走完,至少能解决八成的闪退问题。剩下两成可能是权限问题:脚本里用了Restart-Service、Set-ItemProperty这类命令,普通权限会静默失败,建议用管理员身份运行PowerShell再测一次。还有一种罕见情况是杀毒软件把你刚写的脚本当病毒拦截,执行策略没问题、权限也没问题,但脚本就是跑不起来,此时去Windows安全中心的“保护历史记录”里能看到误报记录。
4.2 “pip不是内部或外部命令”到底是什么原因
这个报错在Windows用户里太经典了。本质是Python安装的时候没把Scripts目录加进系统环境变量Path。在PowerShell里还会提示“无法将pip项识别为 cmdlet、函数、脚本文件或可运行程序的名称”,在cmd里则是“不是内部或外部命令”。两条报错一个意思,都是找不到pip这个可执行文件。
我的建议是,遇到这个报错之后,先用python -m pip代替pip,因为只要python本身能跑,python -m pip就一定能跑,它通过模块方式启动pip,依赖的是python的路径,而不是pip的Scripts路径。之后再一劳永逸修复:找到Python安装目录下的Scripts文件夹,一般在C:\Users\你的用户名\AppData\Local\Programs\Python\Python312\Scripts,手动加到系统环境变量Path里。改完要重新开终端窗口,环境变量才会刷新。
顺便说一句,这个问题和PowerShell执行策略没有关系,纯粹是环境变量没配好。很多新手把两个问题混在一起,结果绕了远路。排查思路要分清楚:命令本身不存在,还是命令存在但被禁止执行,前者看环境变量,后者看执行策略。这两个坑占了Windows脚本问题的一大半。
4.3 路径带空格:一切诡异问题的源头
Windows路径太容易带空格了,比如C:\Program Files\。新手写脚本时最容易犯的错是把路径直接拼在命令行里,不加引号:
# 不推荐 C:\Program Files\Java\jdk-17\bin\java.exe -version # 推荐 & "C:\Program Files\Java\jdk-17\bin\java.exe" -versionPowerShell里调用可执行文件时,路径带了空格就必须加引号,必要时用&调用运算符。同样的问题也出现在Copy-Item、Start-Process的路径参数里。我一般会把所有固定路径都先用变量存起来,使用时用"$path"包一层,这样一眼就能看出字符串边界在哪,避免埋雷。
在计划任务里,这个问题会被放大。如果/TR参数中的路径带空格,而你又忘了整体加引号,任务可能创建成功但启动时报错,或者干脆找不到文件。我见过半夜跑批任务连续失败一周,原因是任务计划里写的路径没加引号,系统把路径从空格处拆成了两段。这种问题光看任务状态根本看不出来,一定要点开任务详情看“上次运行结果”。
4.4 管理员权限缺失,命令静默失败的真相
Windows有个特性:很多系统级命令在非管理员权限下不是报“拒绝访问”,而是干脆没有输出或者假装成功。比如Get-WinEvent读安全日志,普通权限下会提示Access is denied.;sc修改服务配置,可能提示找不到服务。遇到这种情况,最好的做法是在脚本开头主动检测权限:
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "请以管理员身份重新运行本脚本。" exit 1 }这样脚本一进来就给出明确提示,而不是跑到一半才失败。如果想让脚本自己提权,可以写成Start-Process powershell -Verb RunAs -ArgumentList '-File', $MyInvocation.MyCommand.Path,让系统弹UAC确认框。我个人的习惯是:脚本默认不自动提权,只检查权限,告诉使用者“请你手动右键管理员运行”。自动提权看起来很智能,但UAC弹窗经常会把人吓一跳,而且自动化脚本里弹窗无法处理,反而更麻烦。
还有一个细节:即使你是管理员账户,PowerShell窗口未提权时也只是一般权限,因为UAC默认为管理员账户下发过滤令牌。所以判断一个脚本是否需要提权,不能看当前用户是不是Administrator,要看进程令牌里是否包含管理员角色。上面那段代码就是标准做法。
5. 让脚本真正自动化:计划任务、参数传递和日志输出
5.1 用计划任务固定时刻跑脚本
脚本写得再好,如果每天靠手点,意义也有限。Windows的计划任务就是干这个的。命令行创建任务可以用schtasks:
schtasks /Create /TN "CleanTemp" /TR "powershell.exe -ExecutionPolicy Bypass -File D:\Scripts\clean_temp.ps1" /SC DAILY /ST 09:30这条命令创建了一个每天9:30运行的CleanTemp任务。有几个地方容易忽略:
/TR里的命令行如果包含空格,要用双引号整体包住,内部再按需转义;- 计划任务默认以当前用户身份运行,但不会加载你的用户环境变量,所以脚本里如果用到
%USERPROFILE%这类变量,最好改成$env:USERPROFILE或者写绝对路径; - 如果脚本需要管理员权限,
schtasks创建时要用/RU SYSTEM,或者到任务计划程序GUI里勾选“使用最高权限运行”。
图形界面创建更直观:Win+R输入taskschd.msc,创建任务时,触发器选“按计划”,操作选“启动程序”,程序填powershell.exe,参数填-ExecutionPolicy Bypass -File "D:\Scripts\clean_temp.ps1"。设置好之后可以通过schtasks /Query /TN CleanTemp确认。
5.2 参数化脚本,把“写死”变成“通用”
脚本越写越多,你会发现一个规律:很多脚本换一个参数就是另一个场景。用param把关键值提出来,脚本立刻能复用。比如我前面的kill_port.ps1,端口就是参数。再看一个简单例子:
param( [string]$SourceDir = ".", [string]$Prefix = "" )这样脚本既支持默认值,也支持调用时指定参数。使用参数化脚本还能配合计划任务:同一个脚本,可以通过不同参数分别创建清理临时文件、备份某个目录的任务,完全不用复制多个脚本文件。不过要注意,计划任务的参数里如果有空格,要么加引号,要么避免用带空格的目录路径,否则任务会直接失败,而且失败原因特别难找。
参数化还有一个好处:脚本可以被别的脚本调用。比如一个总控脚本遍历多个子任务,内部用& .\detect.ps1 -Phase 2这种方式逐个执行,比每个脚本各自为政好维护得多。把公共逻辑抽成函数或模块,脚本库的复用价值会成倍增长。
5.3 日志输出与错误捕获,别让脚本变成黑盒
脚本自动化之后最怕什么?怕它出了问题你完全不知道。给脚本加日志,三行代码的事:
$logFile = "D:\Logs\job_$(Get-Date -Format 'yyyyMMdd').log" try { # 业务逻辑 Add-Content -Path $logFile -Value "[$(Get-Date)] 任务开始" } catch { Add-Content -Path $logFile -Value "[$(Get-Date)] 执行失败: $_" throw }$_是PowerShell里的当前错误对象,直接转换成字符串就是错误信息。日志每个脚本都应该有,尤其是定时任务脚本。没有日志的定时任务就像一个黑盒,哪天数据出问题你根本不知道是哪一次执行搞坏的。我还会在关键步骤前把将要执行的命令写到日志里,这样复盘的时候能准确还原现场。
另外,日志文件本身也要管理。我见过有人把带日志的脚本跑了一年,日志文件膨胀到几个GB,最后磁盘被写满。建议在脚本里加一个简单的日志清理逻辑:只保留最近30天的日志文件,用Get-ChildItem按日期筛选删除。维护脚本的产物体,和写好脚本本身同样重要。
5.4 开机自启的正确姿势:别把脚本裸奔在启动文件夹
前面说过可以在启动文件夹创建快捷方式,但那是简单场景。如果脚本要稳定运行、能开机前加载、能在系统崩溃后自动重启,就得用计划任务的力量。触发器选“启动时”而不是“登录时”,用户选当前用户并勾选“使用最高权限运行”,条件标签里把“只有在计算机使用交流电源时才启动”勾掉,防止笔记本插着电才执行。
我自己处理设备老化测试脚本时,就是计划任务 + 参数化 + 日志三板斧:定时任务每30分钟调用一次检测脚本,每次检测结果追加到同一份CSV,日志里记录异常。整条链路稳定跑了几个月,唯一一次出问题是磁盘空间被日志占满,后来在脚本里加了日志清理逻辑才解决。这个经历告诉我:自动化脚本不仅要会干活,还要会管自己的产出物。
6. 用好脚本的几条个人习惯
6.1 永远不要把密码明文写进脚本
脚本里最不该出现的就是数据库密码、服务器账号密码。你可以用环境变量、Windows凭据管理器,或者像前面提到的--defaults-extra-file文件。如果实在需要明文,也至少限制文件访问权限,脚本目录不要随意共享给别人。我曾经见过有人把生产库密码直接写进.bat,然后随手发给同事,这个教训代价很高。用环境变量也有讲究,比如MYSQL_PWD设置后,在进程内会有暴露风险,所以环境变量方案适合短期任务,长期任务还是用凭据管理器更稳。
6.2 脚本也要版本管理
我的Scripts目录是纳入Git管理的,每改一次就提交一次。这不是小题大做。Windows脚本写起来快,但迭代也快,如果没有版本记录,改坏了就再也回不去了。哪怕你不懂Git,也可以定期手动把整个目录压缩备份一份,命名加上日期。养成“动脚本之前先备份”的习惯,能避免99%的后悔。尤其是那些已经在计划任务里稳定运行的脚本,改之前一定先备份,改完之后先在测试环境跑一遍,再部署到生产。脚本出问题不会像代码上线一样有明显的报错页面,它往往是安静地跑错,等数据异常时才暴露,那时候定位成本就高了。
6.3 先跑一遍危险操作的危险变体
凡是涉及删除、覆盖、重命名的脚本,第一次执行都必须用-WhatIf或者临时打印命令,让脚本“说”给你听,再让脚本“做”给你看。批量脚本尤其如此。脚本越通用,越要小心边界条件:空目录、超长文件名、只读文件、正在被占用的文件,这些都可能让脚本行为失控。我先在小范围测试,比如只选两个文件跑一遍,确认输出正确,再放开到全量。
除了-WhatIf,我还会在脚本里增加一个“确认模式”开关,默认是只打印不做,后面加-Confirm:$true才需要手动确认。这样即使误执行,也不会造成破坏。这条习惯听起来慢,但脚本一旦跑错影响的是几十甚至几百个文件,花一分钟确认绝对值得。
这些习惯没什么高深道理,但都是我在一次次“脚本跑错、数据遭殃”之后总结出来的。Windows脚本是一门实践手艺,你不需要记住所有命令,只需要把常用场景沉淀下来,慢慢积累,你自己的脚本库里一定会有越来越多能帮你省时间的工具。希望这篇整理能给你一些可复用的起点,少走点我走过的弯路。