简介:《Windows命令行(批处理)语法全解》面向需要编写、维护或查阅 Windows 批处理脚本的运维人员与进阶用户,系统梳理 Command shell 与 PowerShell 两类命令行环境的核心语法,帮助解决脚本命令零散、查询困难的问题。资源为 1 个 PDF 文件,包体约 89.32MB,内置目录与命令索引便于按主题快速定位。内容覆盖批处理常用命令、重定向运算符、for 循环、if 条件与错误处理,并延伸到用户账户管理、夜间备份等自动化场景,注册表配置文件名自动完成、/F:ON 与 /F:OFF 开关、退出代码排查,以及 cmdlet、管道和 Windows 命令的差异。读者可据此快速查阅命令参数、理解批处理控制流程,并将零散知识整理为可复用的脚本模板,减少反复搜索与试错成本。已有 775 人学习,适合作为日常批处理任务书写、命令行知识补漏与脚本排错的案头参考。
1. 一份把 cmd 命令按 A-Z 排好的速查册:它能省掉多少回滚屏时间
凌晨两点改一台离线机器的开机脚本,浏览器打不开、文档站连不上,手边只有一份 PDF,这种场景才最显出一份Windows 命令行(批处理)语法全解的实际价值。它不是教程,是把 Command shell 的命令按字母顺序排好的检索册:目录能跳、命令能查、参数能对,写批处理脚本要查copy、move、for、if、reg的时候,不用在几个网站之间来回翻。适合两类人:一类是刚接手运维脚本、对.bat还停留在“双击能不能跑”阶段的新手;另一类是老手,会写架子但记不住某个参数的全称、子命令层级和退出码含义。后面几章按“怎么查、怎么写、怎么配、怎么排错”的顺序拆,每一步都能直接照着敲。
2. 目录即索引:A-Z 命令表和 shell 选型的落地用法
拿到一份几百条命令的 PDF,最常见的误用是把它当小说从头翻。正确姿势是把它当字典用:先知道自己要解决哪类问题,再顺着首字母和子命令层级往下跳。这一章先把 shell 选型的边界说清楚,再讲三个我常用的检索动作。
2.1 Command shell 与 PowerShell:同一台机器上两条路的边界
Windows 内置两个命令行 shell,这对很多人是常识,但真正落到写脚本时的选型,边界往往比想象中窄。Command shell(也就是cmd.exe)是 Windows 内置的第一个 shell,它的主场是通过批处理.bat文件自动执行日常任务,比如用户账户管理、夜间备份这类“按顺序执行一堆命令”的活。PowerShell 是在它的基础上扩出来的,跑的是 cmdlet,带管道和对象,语言本身可扩展性强得多。
关键的不对称在这里:PowerShell 既能跑 Windows 命令又能跑 cmdlet,而 Command shell只能跑 Windows 命令,跑不了 cmdlet。这意味着你在一台机器上把脚本从.bat迁到.ps1是可行的,反过来把一个依赖Get-ChildItem的脚本塞进.bat里就必然失败。选型上我一般按三条规则判断:
| 场景 | 推荐 shell | 理由 |
|---|---|---|
存量.bat脚本维护、双击即跑 | Command shell | 无需调整执行策略,双击行为确定 |
| 受限环境、只有 cmd 可用 | Command shell | 所有受支持的 Windows 版本都内置 |
| 新写自动化、要处理对象和结构化数据 | PowerShell | cmdlet 面向对象,管道可传递属性 |
| 跨平台复用同一套脚本 | PowerShell Core | 开源版本,可在多种操作系统上运行 |
表格里最后一行是很多人踩过坑的地方:如果同一套逻辑要在非 Windows 环境复用,从第一天就该用 PowerShell Core 写,而不是先写.bat再想办法翻译。反过来,如果目标机器是老系统、或者脚本要嵌进一个只认cmd /c的调用链里,那就老老实实写批处理,别折腾。
还有一点容易被忽略:这两套东西在同一个会话里是可以混的。你完全可以在.bat里调用powershell -NoProfile -Command "...",把一段复杂逻辑外包出去。但我一般只在“确实需要 cmdlet”时才这么干,因为它引入了引号嵌套和转义的复杂度,调试成本陡增。混调之前先在交互式窗口里把单行命令跑通,再抄进脚本,能省掉一半的引号地狱。
2.2 把 A-Z 命令表当检索目录用的三个动作
这份资源最有价值的部分是那个按 A-Z 排列的命令索引。它解决的是“我知道要干什么,但不知道命令叫什么”这个高频痛点。三个具体动作:
动作一:按首字母定位后,一定要看子命令层级。索引里很多条目不是单条命令,而是一棵子树。比如bitsadmin下面挂着addfile、addfilewithranges、cache、cancel、complete、create、getaclflags、setpriority、transfer、util一大串;fsutil下面是8dot3name、behavior、dirty、file、hardlink、quota、reparsepoint、usn等;manage bde下面还有status、on、off、pause、resume、lock、unlock、protectors、tpm、forcerecovery。只看主命令名会让你误以为一条命令搞定,实际要三层才能敲到正确的位置。检索时先扫主条目,再往下找动词。
动作二:按“语法 → 参数 → 备注 → 示例”的固定结构读。命令参考页的结构是稳定的,读的时候不用逐字看。先看语法行确认必填项和可选参数的顺序,再扫参数表找你要的那个开关,备注里通常藏着重写行为、权限要求和副作用,示例照抄改路径最快。我一般的读法是先看示例能不能直接对上我的场景,对不上再倒回去抠参数。
动作三:用相关命令反查。索引是按字母排的,同类功能的命令天然挨着。net print、netstat、netsh、nfsadmin挨在一起,做网络相关的事在这个区间里找;reg、regini、regsvr32、relog、rem挨在一起,做注册表和日志相关的事在这里找。这个“按字母聚簇”的特性,比任何分类目录都稳定,因为命令名本身就是为了定位设计的。
落到实操,第一步永远是确认环境里到底有什么可用:
rem 查看系统版本,确认支持的命令集范围 ver rem 确认 PowerShell 版本,决定能不能用某类 cmdlet powershell -NoProfile -Command "$PSVersionTable.PSVersion" rem 任意命令都可以用 /? 就地拉出语法页,等价于 PDF 里查一次 xcopy /?ver的输出决定了你能不能用某些较新的命令;powershell -NoProfile里的-NoProfile很关键,它会跳过用户配置文件,避免某台机器上装了个奇怪的 profile 把输出改掉。/?是这份 PDF 最好的补充——文档里的参数可能和当前系统版本存在细微差异,/?永远是这台机器上最准的那一份。
提示:命令索引里有不少条目带空格,比如
attributes disk、create partition primary、auditpol set,这些是子命令而不是命令名的空格。检索时按第一个词查,再确认后面的词。
3. 批处理脚本最小可用骨架:变量、if、for 与重定向的落地写法
PDF 里命令是一条条列出来的,但真实的批处理脚本是这些命令的组合。这一章给一个能直接改成自己业务的最小骨架,四个部分:脚本头、条件判断、循环、输出流控制。每一段都能单独拎出来用。
3.1 脚本头四行:@echo off、setlocal 与变量作用域
大部分批处理事故的根源都在前四行没写对。一个能长期用的脚本头是这样的:
@echo off setlocal enabledelayedexpansion title 每日日志归档 cd /d "%~dp0"四行各管一件事。@echo off关掉命令回显,不带@的话这一行本身还会被打印出来。setlocal enabledelayedexpansion开创一个局部变量作用域并打开延迟展开,这是后面循环里读变量能不能拿到新值的前提,也是新手最常漏的一行。title让窗口标题能看懂,多开几个窗口时救命。cd /d "%~dp0"把工作目录切到脚本所在目录,%~dp0展开成脚本的完整路径带尾部反斜杠,/d允许跨盘符切换——不加/d,从C:调用放在D:的脚本,工作目录根本切不过去,所有相对路径全错。
setlocal的另一半是endlocal,但在脚本末尾通常不用手写,脚本结束时会自动还原。真正要小心的是:setlocal之后设置的环境变量不会传回父进程。如果你希望某个变量调用方能看到,得用endlocal & set "OUT=%VAR%"这种写法把它“抬”出去。
变量的赋值习惯也值得一提。我固定用set "VAR=value"这种带引号的写法,而不是set VAR=value。区别在于前者会把引号内的整个内容当作值,行尾多敲一个空格不会被吃进变量里;后者在set VAR=value时,那个尾部空格会原样进变量,后面拼路径就变成一个诡异的“文件找不到”,排查半天。这个坑我吃过不止一次。
3.2 if 与 errorlevel:把判断写成能读的形式
批处理的if有几个变体,用对了可读性不差:
@echo off setlocal call :main set "RC=%ERRORLEVEL%" echo 最终退出码: %RC% exit /b %RC% :main robocopy "D:\data" "E:\backup\data" /MIR /R:2 /W:5 /NP /LOG+:"E:\backup\robocopy.log" if errorlevel 8 exit /b 8 if errorlevel 1 exit /b 0 exit /b 0这段的核心是if errorlevel N的语义:它判断的是“退出码大于等于N”,不是等于。所以顺序必须从大到小写。robocopy的退出码约定是:0 表示没有文件需要复制,1 表示有文件成功复制,2 表示检测到额外文件或目录,3 是 1+2 的组合,4 及以上表示不匹配或失败。上面这段的读法是:8 及以上算真失败,直接往外抛;1 及以上(但小于 8)算正常复制,归零;剩下 0 也归零。这样调用方拿到的退出码才有意义。
%ERRORLEVEL%和if errorlevel的区别也容易翻车。%ERRORLEVEL%是变量展开,在括号块里会被一次性提前展开成进入块之前的值;if errorlevel是运行时判断,不受影响。所以循环或if (...)块里判断退出码,要么用if errorlevel,要么开延迟展开用!ERRORLEVEL!。
其他几个常用的判断形式:
if exist "D:\data\flag.lock" echo 检测到锁文件 if not exist "E:\backup" mkdir "E:\backup" if defined BUILD_ID echo 构建号: %BUILD_ID% if /i "%MODE%"=="prod" echo 生产模式if /i做大小写不敏感比较,环境变量比较一定要加,否则PROD和prod会被判成不同。if defined判断变量是否存在,比if "%VAR%"==""更安全,因为后者在变量值恰好包含引号时会解析出错。
3.3 for 的三种形态:文件、目录、数字区间
for是批处理里最值得花时间吃透的东西。常见的有四种形态,覆盖 90% 的场景:
rem 形态一:遍历当前目录下的文件 for %%f in (*.log) do echo 文件名: %%~nxf rem 形态二:遍历目录 for /d %%d in ("D:\data\*") do echo 目录: %%~nxd rem 形态三:递归遍历 for /r "D:\data" %%f in (*.tmp) do del /q "%%f" rem 形态四:解析命令输出,按逗号切列 for /f "tokens=1,3 delims=," %%a in ('type "D:\data\sales.csv"') do ( echo 订单号 %%a,金额 %%b )形态四的细节最多,值得单独说。tokens=1,3表示取第 1 列和第 3 列,第一个取到的列映射到%%a,第二个自动映射到%%b——注意是按顺序自动分配字母,不是按你写的数字。delims=,指定分隔符,如果想同时用空格和逗号切,写delims=,(逗号后面跟一个空格)。如果整行都要,不切分,用"delims=",这是处理含空格路径的标准写法。
还有两个修饰符经常用:skip=1跳过首行表头,eol=#指定注释行前缀。合起来是for /f "skip=1 tokens=1,3 delims=," %%a in (...) do (...)。
需要提醒的是%%f和%f的区别。写在.bat文件里用双百分号,直接在命令行窗口里敲用单百分号。这个规则没有例外,抄代码时忘记改会直接报错或行为不对。
%%~系列修饰符也值得记:%%~nf取文件名不含扩展名,%%~xf取扩展名,%%~pf取完整路径,%%~zf取文件大小,%%~tf取时间戳。%%~dp0就是脚本自身目录,前面已经用过。组合起来%%~dpnf是“所在目录 + 文件名不含扩展名”。
3.4 重定向与管道:三个符号改写输出流
命令行的输出控制符号不多,但顺序和组合很讲究:
rem 标准输出覆盖写入 dir "D:\data" > "D:\out\dir.txt" rem 标准输出追加 dir "D:\data" >> "D:\out\dir.txt" rem 标准错误单独捕获 robocopy "D:\data" "E:\backup" /MIR 2> "D:\out\error.txt" rem 标准输出和标准错误合并到同一个文件(顺序不能反) somecommand.exe > "D:\out\all.txt" 2>&1 rem 管道把上一个命令的输出喂给下一个 type "D:\out\all.txt" | findstr /i "error" | more2>&1的位置是血泪经验。> file 2>&1是对的:先把标准输出重定向到文件,再把标准错误指向标准输出当前的目标(也就是那个文件)。反过来写成2>&1 > file,标准错误会指向重定向之前的标准输出,也就是控制台,结果是错误信息刷屏、文件里只有正常输出——这个错误非常隐蔽,因为脚本看起来跑完了。
管道|会把左侧命令的标准输出接到右侧命令的标准输入。注意它不传递退出码:a.exe | b.exe之后%ERRORLEVEL%拿到的是b.exe的退出码。如果你关心a.exe是否失败,要么把输出先落盘再处理,要么启用setlocal enabledelayedexpansion配合cmd /v:on做中间捕获,实操上后者更麻烦,前者更稳。
>和>>的区别看似简单,但覆盖模式下,如果目标文件被别的进程占用,命令会直接失败并且先把文件截断。所以日志文件永远用>>,配置输出永远先写临时文件再move覆盖,这个习惯能省掉很多“日志写到一半没了”的事故。
4. 命令补全的两个注册表键:CompletionChar 与 PathCompletionChar 实测配置
补全这个功能,用惯了的人离不开,不知道的人写了十年批处理也没开过。PDF 里提到了它的配置方式,但只给到“改哪两个键”,实际落地还有优先级和回滚的问题。
4.1 补全是怎么生效的:注册表、命令行开关与优先级
Command shell 可以把某个控制字符绑定成“自动补全文件和目录名”的触发键。默认是 Tab,但它是可配置的。配置来源有三层,优先级从高到低是:命令行开关 > 用户级注册表 > 机器级注册表。
用户级在HKEY_CURRENT_USER\SOFTWARE\Microsoft\Command Processor下,机器级在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor下,各有两个值:
| 值名 | 作用 | 类型 |
|---|---|---|
CompletionChar | 文件名补全的触发字符 | REG_DWORD |
PathCompletionChar | 目录名补全的触发字符 | REG_DWORD |
值就是虚拟键码,十六进制或十进制都行。命令行开关那条路是cmd.exe /F:ON和/F:OFF:开启时,两个控制字符被固定为 Ctrl-D 做目录补全、Ctrl-F 做文件名补全;关闭时禁用。用户自己设的值优先于机器级,命令行开关优先于注册表。
这里有个容易困惑的点:/F:ON和注册表设的值是两套东西,/F:ON不看你的注册表配置,直接硬编码成 Ctrl-D 和 Ctrl-F。所以如果你把注册表改成别的字符,又用/F:ON启动,实际生效的会是 Ctrl-D/Ctrl-F 而不是你设的。
要禁用某个补全字符,把值设成空格0x20(十进制 32)。理由很实在:空格不是有效的控制字符,不会被误触发,而且比删键值更好回滚——你知道自己改过什么。
4.2 用 reg add 改键值:一张参数对照表
改之前先备份,这一步别省:
rem 备份当前用户的 Command Processor 配置 reg export "HKCU\SOFTWARE\Microsoft\Command Processor" "%USERPROFILE%\Desktop\cmdproc-backup.reg" /y rem 先查当前值,确认改之前是什么 reg query "HKCU\SOFTWARE\Microsoft\Command Processor" /v CompletionChar reg query "HKCU\SOFTWARE\Microsoft\Command Processor" /v PathCompletionCharreg export的/y是覆盖确认,不加会弹交互。备份文件放在桌面是为了好找,生产环境建议固定到一个配置仓库目录。
然后写入。因为CompletionChar管文件名、PathCompletionChar管目录名,而/F:ON的约定是 Ctrl-F 补文件、Ctrl-D 补目录,所以想和/F:ON行为对齐的话:
rem Ctrl-F (0x06) 触发文件名补全 reg add "HKCU\SOFTWARE\Microsoft\Command Processor" /v CompletionChar /t REG_DWORD /d 0x06 /f rem Ctrl-D (0x04) 触发目录名补全 reg add "HKCU\SOFTWARE\Microsoft\Command Processor" /v PathCompletionChar /t REG_DWORD /d 0x04 /f rem 只想保留 Tab 补全的话,两个都设 0x09 rem reg add "HKCU\SOFTWARE\Microsoft\Command Processor" /v CompletionChar /t REG_DWORD /d 0x09 /f rem 想禁用某个补全,用空格 0x20 rem reg add "HKCU\SOFTWARE\Microsoft\Command Processor" /v PathCompletionChar /t REG_DWORD /d 0x20 /f参数逐个说清:/v指定值名,/t REG_DWORD指定类型——类型写错是这里最常见的失败原因,写成REG_SZ系统读的时候类型不匹配,功能静默失效且不报错。/d给数据,0x06这种带前缀的按十六进制解析,也可以写十进制6。/f强制覆盖已有值,不加会问一句。
常见控制字符对照:
| 按键 | 十六进制 | 十进制 | 备注 |
|---|---|---|---|
| Tab | 0x09 | 9 | 系统默认 |
| Ctrl-D | 0x04 | 4 | /F:ON的目录补全键 |
| Ctrl-F | 0x06 | 6 | /F:ON的文件名补全键 |
| 空格 | 0x20 | 32 | 禁用该补全 |
| Ctrl-G | 0x07 | 7 | 不与常见快捷键冲突,可作备选 |
选字符时避开已经在用的组合键:Ctrl-C 是中断、Ctrl-Z 是输入结束、Ctrl-S 在某些终端里是流控。Ctrl-D 和 Ctrl-F 之所以被官方选为/F:ON的默认值,就是因为它们在 cmd 里基本没别的用途。
4.3 验证与回滚:改完怎么确认它真的工作了
注册表改完,已经开着的 cmd 窗口不会生效,必须新开一个。这一点和很多人的直觉相反,导致改了没反应就以为配置错了。
验证脚本:
rem 重新查询,确认值确实写进去了且类型是 REG_DWORD reg query "HKCU\SOFTWARE\Microsoft\Command Processor" /v CompletionChar reg query "HKCU\SOFTWARE\Microsoft\Command Processor" /v PathCompletionChar rem 启动一个显式开启补全的会话做对照 cmd /F:ONreg query的输出会带类型,形如CompletionChar REG_DWORD 0x6。如果看到REG_SZ,说明类型写错了,用reg delete删掉重加:
reg delete "HKCU\SOFTWARE\Microsoft\Command Processor" /v CompletionChar /f验证行为的方法很直接:在新开的窗口里敲cd C:\Win然后按你设的目录补全键,看是否展开成C:\Windows;敲dir C:\Windows\notep按文件名补全键,看是否补成notepad.exe。两件事都成立才算配置成功。
回滚有两条路。精细回滚是删掉你加的键:
reg delete "HKCU\SOFTWARE\Microsoft\Command Processor" /v CompletionChar /f reg delete "HKCU\SOFTWARE\Microsoft\Command Processor" /v PathCompletionChar /f整体回滚是导入之前的备份:
reg import "%USERPROFILE%\Desktop\cmdproc-backup.reg"reg import会合并而不是覆盖整个键,所以如果备份之后又加过别的值,那些值会保留。想彻底还原,先reg delete整个键再导入。
注意:编辑注册表不当可能对系统造成严重损坏,改动前务必备份相关值数据。改机器级(
HKLM)时要管理员权限,而且会影响这台机器上所有用户,多人共用的机器上优先改用户级。
5. 避坑与排查:批处理翻车的五个现场
这一章的五条都是我在真实脚本里踩过的,按“现象 → 原因 → 解决”写,遇到同类问题可以直接对号入座。
5.1 循环里 set 进去的变量,echo 出来永远是旧值
现象:在for循环里set "N=%%f",紧接着echo %N%,输出的永远是循环开始前的值,像是赋值没生效。
原因:批处理在执行一个括号块之前会做一次变量展开,%N%在进入块的那一刻就被替换成旧值了,循环里再赋值也不会重新展开。这是 cmd 解析器的行为,不是 bug。
解决:脚本头加setlocal enabledelayedexpansion,循环里改用!N!读取。延迟展开会让!在运行时才求值。如果变量值本身包含!,需要临时setlocal disabledelayedexpansion包一层,或者用call做二次展开绕开。
5.2 路径里带空格,整条命令直接报参数过多
现象:copy D:\My Data\a.txt E:\backup\报“命令语法不正确”或“参数过多”,但路径明明存在。
原因:空格被当成了参数分隔符,D:\My成了第一个参数,Data\a.txt成了第二个。
解决:所有路径一律加双引号,包括看起来绝对不会有空格的。脚本里接收外部参数时用"%~1",~会去掉调用方可能加的多余引号再重新加一对,避免出现""path""。变量拼接路径时写"%ROOT%\%NAME%"而不是%ROOT%\%NAME%。
5.3 日志里的中文全变成乱码
现象:用echo 处理完成 >> log.txt写日志,用记事本打开显示乱码,或者反过来在 cmd 窗口里type一个 UTF-8 文件全是问号。
原因:cmd 的当前代码页和文件实际编码不一致。默认代码页是 936(GBK),如果文件存成了 UTF-8,读出来就乱。
解决:两条路选一条走到底。要么统一用 GBK——脚本文件本身存成 ANSI/GBK,不写chcp,最省事;要么统一用 UTF-8——脚本开头chcp 65001 >nul,脚本文件存成无 BOM的 UTF-8。特别注意不能存成带 BOM 的 UTF-8,因为 BOM 的三个字节会被当成命令的一部分,第一行直接报错。跨机器分发脚本时我倾向 GBK 方案,因为不用赌对方终端的代码页支持。
5.4 双击运行,窗口一闪就没了
现象:在 cmd 窗口里跑得好好的脚本,双击运行后窗口瞬间消失,什么都看不到。
原因:脚本执行完毕后 cmd 会话立即结束。如果中间有exit(不带/b),会直接终止整个 cmd 进程,后续的pause都不会执行。
解决:排查阶段在末尾加pause,或者干脆从 cmd 窗口里cd到脚本目录手动跑。脚本内的退出统一用exit /b <code>而不是exit,前者只退出当前批处理上下文,后者会杀掉整个 shell。日志重定向也能帮忙:脚本.bat > run.log 2>&1,窗口关了日志还在。
5.5 注册表改完,Tab 补全还是没反应
现象:按 4.2 的脚本写入了CompletionChar,新开窗口按 Tab 还是不能补全文件名。
原因:排查下来通常有三种。一是只改了HKLM,但HKCU下还留着旧值,用户级优先,实际生效的是旧值;二是值的类型写成了REG_SZ,读的时候类型不匹配静默失效;三是当前会话是用cmd /F:OFF启动的,命令行开关优先级最高,把注册表配置整个盖掉了。
解决:reg query两个位置都查一遍,确认类型全是REG_DWORD;确认没有/F:OFF在启动参数里;确认是新开的窗口。三条都排掉还不行,就删掉HKCU下的键回到系统默认,先确认 Tab 能用,再逐步加配置。
6. 进阶:把这份 PDF 当黑匣子用,靠退出码和输出反查真实行为
最后说一个我用了很久的技巧:这份文档最容易被低估的部分,不是命令列表,而是它对退出和错误代码的指向。批处理脚本要写得可靠,核心不是命令背得多,而是每条命令执行完你都知道它到底成功没有。
一个自制的探针脚本,用来一次性拿到命令的退出码和输出:
@echo off setlocal if "%~1"=="" ( echo 用法: rc-probe.bat "要探测的命令" exit /b 64 ) %* > "%~dp0probe.out" 2> "%~dp0probe.err" set "RC=%ERRORLEVEL%" echo 退出码: %RC% for %%f in ("%~dp0probe.err") do if %%~zf gtr 0 ( echo --- stderr --- type "%~dp0probe.err" ) exit /b %RC%用法是rc-probe.bat robocopy "D:\data" "E:\backup" /MIR。它会跑命令、落盘标准输出和标准错误、打印退出码,只在标准错误非空时才把它打出来。%*是全部参数原样传递,注意它不会帮你补引号,调用时该加的引号还得加。%%~zf取文件大小,用来判断标准错误是否为空——比if exist更准,因为2>会创建空文件。
拿到退出码之后,回到 PDF 里查“调试系统错误代码”那一节对照含义。这一步常被跳过,但它能区分“命令拒绝了请求”和“命令根本没找到目标”这两类完全不同的问题。前者通常要改权限或参数,后者通常是路径写错。
更进一步,可以建一张自己项目的退出码表,把脚本的退出码也规范化:
| 退出码 | 含义 | 处理方式 |
|---|---|---|
| 0 | 成功,无需处理 | 静默 |
| 1 | 成功,有变更 | 记日志 |
| 2 | 参数错误 | 打用法,立即退出 |
| 8 | 部分失败 | 告警,继续 |
| 16 | 致命错误 | 中止,触发告警 |
这张表的意义在于:调用方(另一个脚本、任务计划程序、CI)能根据数字判断要不要重试、要不要告警,而不是靠 grep 日志里的中文。批处理本身的字符串处理能力很弱,用退出码通信是最稳的方式。
还有一个配合用法是for /f解析命令输出做决策。比如用sc query查服务状态,用tasklist /fi查进程,把结果喂给findstr判断,比在批处理里写复杂的逻辑判断干净得多:
set "STATE=" for /f "tokens=3" %%s in ('sc query "Spooler" ^| findstr /i "STATE"') do set "STATE=%%s" if /i "%STATE%"=="RUNNING" (echo 服务在运行) else (echo 服务状态: %STATE%)注意管道在for /f的单引号里要转义成^|,这是 cmd 解析顺序决定的,不转义会被外层当成管道处理。
从那以后我每次写.bat,都强制先把三样东西写死在模板里:setlocal enabledelayedexpansion、cd /d "%~dp0"、以及所有路径带引号。这三条挡掉的问题,比我后来学的任何高级技巧都多。希望帮到你。
本文还有配套的精品资源,点击获取