Contents

批处理语法整理

参考资料

常用语句

Windows 系统的批处理文件扩展名为 .bat.cmd,通过 Cmd.exe 逐行解释执行,对大小写不敏感
C:\AUTOEXEC.BAT 在系统启动时会自动运行,可以将每次启动时都要运行的命令放入该文件中

脚本配置

1
2
3
4
@echo off          :: 隐藏命令回显
chcp 65001 >nul    :: UTF-8编码(静默设置)
title 我的脚本      :: 窗口标题
color 0A           :: 黑底绿字(可选)

路径操作

1
2
3
4
%~dp0           # 批处理文件所在目录
cd %~dp0        # 进入批处理文件目录
cd %~dp0bin\    # 进入 bin 子目录
cd /d "C:\Program Files"  :: 切换驱动器并进入目录

其中 d 为 Drive 的缩写,即为驱动器,磁盘;p 为 Path 缩写,即为路径,目录

1
2
3
%~nx0        :: 文件名和扩展名
%~1          :: 去掉引号的第一个参数
%~f1         :: 第一个参数的完整路径

重定向

1
2
3
4
>nul         :: 丢弃标准输出
2>nul        :: 丢弃错误输出
>nul 2>&1    :: 将所有输出都丢弃
>> file.log  :: 追加到文件

变量操作

系统变量

1
2
3
echo 系统根目录: %SystemRoot%    :: C:\Windows
echo 临时目录: %temp%              :: 临时文件夹路径
echo 用户名: %username%           :: 当前用户

自定义变量

1
2
3
set name=张三
set /a age=20+1           :: 算术运算
set /p input=请输入:      :: 用户输入

参数处理

参数下标从 1 开始,通过百分号加数字表示

1
call test.bat "hello" "haha"
1
2
3
4
5
6
::test.bat
echo %1  ::"hello",对应传入的第一个参数
echo %2  ::"haha"
echo %0  ::脚本名 test.bat
echo %19  ::"hello"9
echo %*  ::所有参数

字符串处理

截取字符串

1
2
3
4
5
set str=HelloWorld
echo 前5位: %str:~0,5%        :: Hello
echo 第3位开始: %str:~2%        :: lloWorld
echo 倒数第3位开始: %str:~-3%       :: rld
echo 第2到倒数第2: %str:~1,-1% :: elloWorl

替换字符串

1
2
3
4
5
set str=hello world
echo %str%

:: 将 world 替换为 bat
set str=%str:world=bat%     :: "hello bat"

字符串长度

通过循环获取字符串长度

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
::函数定义和初始化
::定义一个名为 strlen 的标签(函数),接受两个参数
:strlen <result> <string>
::启用延迟变量扩展,允许在代码块中使用 !var!
setlocal enabledelayedexpansion
::将第二个参数(要去掉引号)赋值给 str 变量,^ 防止特殊字符被解释
(set ^str=%~2)

::空字符串检查
::如果输入字符串为空,将结果变量设置为 0 并退出函数
if "!str!"=="" (
    set "%~1=0"
    exit /b
)

::初始化长度
set /a len=0
:loop
::主循环,如果当前字符串长度为空,则使用 endlocal 结束局部环境,将长度值传递给调用者,然后退出
if "!str!"=="" (
    endlocal & set "%~1=%len%"
    exit /b
)
::否则长度加 1,移除字符串的第一个字符,继续循环
set /a len+=1
set "str=!str:~1!"
goto loop

使用方法

1
2
call :strlen length "Hello World"
echo 字符串长度是: %length%

流程控制

条件判断

1
2
3
4
5
6
7
8
9
:: 字符串比较
if "%var%"=="value" (echo 相等)

:: 数值比较
if %number% gtr 100 (echo 大于100)

:: 文件检查
if exist "file.txt" (echo 文件存在)
if not defined var (echo 变量未定义)
  • 多条件分支
1
2
3
4
5
6
7
if "%1"=="hello" (
    echo 参数是hello
) else if "%1"=="world" (
    echo 参数是world
) else (
    echo 未知参数
)
  • 错误码判断
1
2
3
4
5
6
7
8
9
:: ❌ 错误的写法(常见误区)
if errorlevel 1 echo 错误1
if errorlevel 2 echo 错误2
if errorlevel 3 echo 错误3

:: ✅ 正确的写法(必须从大到小)
if errorlevel 3 echoecho 错误3
if errorlevel 2 echo 错误2
if errorlevel 1 echo 错误1

若错误码 errorlevel(或称返回码)等于指定的数字,则条件成立,运行指定命令,否则跳过,运行下一句
if errorlevel n 的意思是"如果错误级别 大于等于 n",所以返回值必须按照 从大到小 的顺序排列

  • 比较运算符表
运算符 描述 替代写法
EQU 等于 ==
NEQ 不等于 if not 1==1
LSS 小于
LEQ 小于或等于
GTR 大于
GEQ 大于或等于

循环结构

基本格式: for [参数] %%变量 in (集合) do (命令)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
:: 显示当前目录下bat或txt文件
for %%i in (*.bat *.txt) do (echo %%i)

:: 递归显示E盘test目录下文件
for /r E:\test %%i in (*.txt *.bat) do (echo %%i)

:: 显示包含e或i的目录名
for /d %%i in (*e* *i*) do (echo %%i)

:: 递归遍历所有文件
for /r %%i in (*) do (echo %%i)

:: 数字序列1-5
for /l %%i in (1,1,5) do (echo %%i)
  • 文本处理
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
:: 解析日期
for /f "tokens=1-3 delims=-/. " %%j in ('Date /T') do echo %%j年%%k月%%l日

:: 读取文件内容
for /f "delims=" %%i in (zhidian.txt) do (echo %%i)

:: 去除每行前8个字符
setlocal enabledelayedexpansion
for /f %%i in (zhidian.txt) do (
    set atmp=%%i
    set atmp=!atmp:~8!
    if {!atmp!}=={} (echo.) else (echo !atmp!)
)

跳转标签

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
:: 简单跳转
goto :label

:: 条件跳转
if errorlevel 1 goto :error

:: 函数式跳转
call :function_name
exit /b

:function_name
echo 这是一个函数
exit /b 0

指定跳转到 label 标签行,找到标签行后,程序将处理从 下一行开始 的命令
label 标签的名字最好是有意义的,字母前必须加个 冒号 : 来表示这个字母是标签
经常与 if 配合使用,根据不同的条件来执行不同的命令组

程序调用

调用其他批处理

调用 另一个 批处理程序,并且 不终止 当前的父批处理程序
如果不用 call 而直接调用别的批处理文件,那么执行完那个批处理文件后 将无法返回 当前文件并执行当前文件的后续命令
无法在脚本或批处理文件外使用 call

1
2
call other.bat arg1 arg2    # 调用后会返回
other.bat arg1 arg2         # 调用后不返回

启动外部程序

调用外部程序,所有的 DOS 命令命令行程序 都可以由 start 命令来调用

1
2
start "C:\Program Files\app.exe" arg1 arg2
start /min e:\"program files"\notepad.exe      # 开始时窗口最小化

文件路径名有空格时,就利用双引号

实用技巧

用户交互

1
2
3
:: 简单输入
set /p choice=请选择(Y/N):
if /i "%choice%"=="Y" goto yes
1
2
3
4
5
:: 选项选择
choice /c ync /m "确认执行吗"
if errorlevel 3 goto cancel
if errorlevel 2 goto no
if errorlevel 1 goto yes

choice 命令可以让用户输入一个字符(用于选择),从而根据用户的选择返回不同的 errorlevel(choice 的返回值是环境变量 errorlevel 的值)
然后配合 if errorlevel 选择运行不同的命令

并且限定用户只能输入"y"、“n”、“c"这三个选项中的一个
当你选择 y 时,y 是第 1 个选项,所以 errorlevel 的值为 1
当你选择 n 时,n 是第 2 个选项,所以 errorlevel 的值为 2
当你选择 c 时,c 是第 3 个选项,所以 errorlevel 的值为 3

进程管理

1
2
3
4
5
6
:: 结束进程
taskkill /f /im notepad.exe >nul 2>&1

:: 检查进程是否存在
tasklist /fi "imagename eq notepad.exe" | find "notepad.exe" >nul
if errorlevel 1 (echo 进程不存在) else (echo 进程运行中)

/f 参数表示强制终止进程,即无需提示用户确认即可终止进程
/im 参数后面跟着的是进程的映像名称(Image Name),用于指定要终止的进程
在命令中的 ??? 部分需要替换为具体的进程映像名称,例如 notepad.exe 或 chrome.exe
使用 taskkill 命令终止进程需要管理员权限

示例模板

基础脚本模板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@echo off & chcp 65001 >nul
title 批处理脚本 - %~n0

echo [INFO] 脚本启动时间: %date% %time%
echo [INFO] 工作目录: %cd%

:: 参数验证
if "%~1"=="" (
    echo [ERROR] 缺少参数
    echo 用法: %0 ^<参数^>
    pause
    exit /b 1
)

call :main %*
goto :eof

:main
    echo 主逻辑开始...

    :: 业务代码在这里

    echo 主逻辑结束...
    exit /b 0

菜单驱动脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@echo off
:menu
cls
echo ============ 主菜单 ============
echo 1. 备份文件
echo 2. 清理临时文件
echo 3. 系统信息
echo 0. 退出
echo.

choice /c 1230 /n /m "请选择:"
goto option%errorlevel%

:option1
    echo 执行备份...
    goto menu

:option2
    echo 清理临时文件...
    goto menu

:option3
    systeminfo | findstr /c:"OS 名称" /c:"系统类型"
    pause
    goto menu

:option4
    exit /b 0

调试技巧

  • 在关键位置添加 echo 输出调试信息

    echo 命令类似于 print 函数,也可以配合重定向符号使用,将输出的内容写入文件中

    echo off 使得命令行窗口 在其之后的命令 只显示脚本运行结果,而不会显示每个命令的具体执行过程。用于隐藏命令行窗口中的冗余输出,使输出结果更加简洁
    @ 符号表示不显示 当前该行 的命令
    所以常常将二者结合使用:@echo off

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
:: 启用变量延迟扩展
setlocal enabledelayedexpansion

:: 调试输出
echo [DEBUG] 变量值: !var!
echo [DEBUG] 当前目录: %cd%

:: 暂停查看,屏幕上显示 `Press any key to continue...` 的提示,等待用户按任意键后继续
pause
:: pause 命令会使程序挂起

:: 记录日志
echo %date% %time% - 操作完成 >> script.log

在批处理中,变量默认是在 一行命令解析前 就被替换了
启用延迟扩展后,可以使用 !var! 来在命令执行时动态获取变量值

  • 为什么需要延迟?

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    :: 没有延迟扩展的问题
    set count=0
    for /l %%i in (1,1,5) do (
        set /a count+=1
        echo %count%    :: 这里永远显示0!
    )
    
    :: 使用延迟扩展的正确方式
    setlocal enabledelayedexpansion
    set count=0
    for /l %%i in (1,1,5) do (
        set /a count+=1
        echo !count!    :: 这里会正确显示1,2,3,4,5
    )
    
1
2
rem 这是注释
:: 这也是注释(更常用)
1
2
3
4
5
:: 批处理中使用 ^ 换行
echo 这是一段很长的文本,^
在换行了

:: Shell脚本中使用 \ 换行
1
2
命令名 /?
:: 例如: ping /?

错误处理

1
2
3
4
5
6
:: 检查命令是否成功执行
some_command >nul 2>&1
if errorlevel 1 (
    echo [ERROR] 命令执行失败
    exit /b 1
)