一、为什么 2024 年还在写 bat 脚本
2014 年的 Windows 自动化基本是 bat + VBScript 双雄;2018 年之后 PowerShell Core 跨平台,bat 看起来"该退休"了。但实际场景里:
- 老旧 Windows Server 2008 R2 / 2012 R2 默认没装 PowerShell 5,bat 仍是首选
- 计划任务里挂的"老脚本"没人敢动,出问题了又得维护
- NSSM 注册服务的某些参数只能传 bat 路径
- 第三方工具的安装脚本(很多 CAD / 工控软件)就是 bat,你绕不开
所以"bat 找 PID + 杀进程"是必须会的硬功夫。本文以两个高频需求切入:
- 找到后台跑的 bat 进程(常见:监控 / 重启脚本卡死)
- 结束指定进程(常见:清残留)
二、找后台运行的 bat 进程
2.1 用 wmic 找 bat 进程
| |
逐步拆解:
wmic process get commandline,processid—— 列出所有进程的"完整命令行"和"PID"findstr 监控重启.bat—— 过滤出命令行里含"监控重启.bat"的行findstr /v findstr—— 排除掉 “findstr 自身” 那一行(否则你会看到 findstr 自己的命令行)
输出形如:
| |
第二列的 4620 就是 bat 对应的 PID(其实是 cmd.exe 的 PID,因为 bat 是 cmd 解释执行的)。
2.2 用 PowerShell 等价(推荐)
| |
PowerShell 的优势:
- 对象化输出,可以直接
| Stop-Process一气呵成 - 不像 wmic 那样返回字符串,不依赖解析列宽
- Unicode 中文文件名不会乱码
wmic 在 Win 10 21H1 起开始被标记为 deprecated(最终会从 Windows 中移除),强烈建议新写的脚本用 PowerShell CIM。
2.3 用 Get-Process 按名字找(不推荐)
| |
缺点:cmd.exe 的所有实例都返回(10 个 cmd 窗口就有 10 条),需要再过滤窗口标题,对纯后台运行的 bat 不适用。
三、结束进程
3.1 taskkill(cmd 原生)
| |
参数说明:
/f—— 强制结束(不弹确认)/pid—— 按 PID/im—— 按镜像名(taskkill /f /im cmd.exe会杀掉所有 cmd 窗口,慎用)
3.2 杀进程并确认
| |
3.3 PowerShell 一行杀
| |
或者用更直观的 Get-Process:
| |
四、实战:监控重启脚本
4.1 需求
写一个 bat 监控某个进程(如 myapp.exe),如果挂了自动重启。
4.2 朴素版(轮询)
| |
关键点:
tasklist | findstr /i—— 查进程是否存在if %errorlevel% == 0—— 找到就是 0start ""—— 空标题必须,否则会报错timeout /t N /nobreak—— 等 N 秒,不响应 Ctrl+C(比ping -n更精确)
4.3 改进版(按需触发)
如果只想要"挂了再启动,不轮询",可以用 WaitForSingleObject 思路,但 bat 实现不了,这种场景请用 nssm 注册成 Windows 服务(详见 0.4 批次的 2022-12-15《Windows 系统管理员实战》)。
4.4 用 PowerShell 写监控脚本
| |
PowerShell 优势:
Get-Process比tasklist | findstr更可靠(处理特殊字符不挂)Start-Sleep比timeout更可编程- 异常可以用
try/catch包裹
五、批处理编码与中文乱码
5.1 bat 文件默认编码
bat 在 中文 Windows 下默认是 GBK 编码。如果你用 UTF-8 编码的 bat 直接运行,中文会乱码。
两种处理方式:
- 保存为 GBK / ANSI 编码(最稳)—— 用 VS Code 右下角"UTF-8"点开选"Save with Encoding"→“GBK"或"Chinese Simplified (GB2312)”
- 保存为 UTF-8 BOM 编码 —— Windows 10 1809+ 能识别 BOM 自动用 UTF-8 解释
- 在 bat 顶部加
chcp 65001 >nul—— 切换控制台代码页到 UTF-8,但这不会改变 bat 源文件的解释编码,对 bat 里的中文 echo 没有帮助,仅对 bat 调用的外部命令的输出有影响
5.2 输出 UTF-8 到文件
| |
注意:> 重定向输出的 output.txt 会带 UTF-8 BOM(如果 cmd 默认按 ANSI 解释 echo 命令后再输出)。最稳的办法是用 PowerShell:
| |
5.3 bat 里包含中文路径
| |
中文路径用双引号包起来就不会出错。/d 开关让你不用先 D: 切盘符。
六、批处理常见错误与排查
6.1 错误级别(%errorlevel%)
每条命令执行后,cmd 设置一个 errorlevel:
0—— 成功非 0—— 失败(具体值看命令)
| |
6.2 延迟扩展(setlocal enabledelayedexpansion)
bat 里的 if 块、for 块默认不实时展开变量。典型坑:
| |
输出:
| |
因为 %COUNT% 在 for 块开始时就被展开为 0,后续 set /a 修改的 COUNT 没被重新读取。
正确写法:
| |
关键点:
- 顶部加
setlocal enabledelayedexpansion - 块内用
!VAR!而非%VAR%来读取最新值
6.3 路径里的空格
| |
用双引号包路径最稳。第二种用环境变量展开也 OK,但路径里有 & 之类特殊字符必须用引号。
七、批处理的替代方案
| 场景 | 替代方案 |
|---|---|
| 简单文件操作 / 启停服务 | bat 仍是最简方案 |
| 跨平台脚本 | PowerShell Core / pwsh |
| 复杂文本处理 | PowerShell / Python |
| 长任务调度 | Windows 任务计划程序 + bat/ps1 |
| 常驻服务 | nssm 注册成服务(详见 2022-12-15 那篇) |
| 配置中心 | Ansible(WinRM 协议) |
八、常见 5 个坑
wmic在新 Windows 上 deprecated——新写脚本用Get-CimInstance。- 中文乱码——bat 源文件保存为 GBK / ANSI 编码最稳。
for块内%VAR%不刷新——用setlocal enabledelayedexpansion+!VAR!。taskkill /f /im cmd.exe把所有 cmd 窗口都杀了——按 PID 杀更精准。- bat 里的"后台进程"实际是
cmd.exe——wmic 找到的 PID 是 cmd 解释器的,杀它才能停 bat。
九、总结
- 找后台 bat 进程:
wmic process get commandline,processid | findstr xxx.bat(老方案);Get-CimInstance(推荐) - 杀进程:
taskkill /f /pid PID(cmd);Stop-Process(PowerShell) - 监控 + 自动重启:
tasklist | findstr轮询(朴素);nssm注册服务(推荐) - 中文乱码:bat 源文件保存为 GBK / ANSI最稳
- 延迟变量:
setlocal enabledelayedexpansion+!VAR! - 新写脚本优先用 PowerShell(pwsh 跨平台),bat 留给"老系统 / 老脚本维护"
