End

BAT 批处理 基础语法 教程

博文地址


目录

BAT 批处理教程

参考:易百教程
参考:批处理之家

当Windows为我们打开了五彩缤纷的图形窗口的时候
DOS命中注定会陨落
CMD毫无悬念将萎缩
批处理逐渐趋向无声无息
而powershell的到来,无疑会让更多的人忘记批处理
这是一门即将失传的技艺
这是一块行将就木的领域
然而,命令行工具仍然具有批量处理一切的巨大威力
字符界面仍然是高效操作、方便灵活、简洁快速的代名词

基础语法

特点

  • 批处理脚本存储在文本文件中,其中包含的命令按顺序依次执行,其功能是为了自动执行重复的命令序列
  • 批处理文件具有特殊的扩展名BAT或CMD,可以通过双击、或在命令提示符(cmd.exe)或在开始 - 运行行中运行
  • 批处理文件通过称为命令解释器的系统文件提供的接口(shell)来识别和执行,在Windows系统上是cmd.exe
  • 批处理文件可以读取用户的输入,有if、for等控制结构,支持函数、数组等高级功能,支持正则表达式,可以包含其他编程代码

编码问题

在windows上,bat文件默认以GBK编码格式保存(很多开发语言、环境的默认编码为UTF-8格式),如果文件中有中文或特殊符号,强烈建议以GBK编码格式保存,否则会因各种各样的问题导致乱码进而导致执行出错!

如果不能修改文件编码格式,例如通过 for 等命令读写其他编码格式的文件,那需要使用CHCP修改编码格式。具体可用的编码参考 Code Page Identifiers

@echo off & CHCP 65001
:: 会在命令行中输出【Active code page: 65001】的提示

注释

注释有三种:

  • Rem 注释内容:Rem相当于命令,如果Rem的行数太多,可能会使代码变慢
  • :: 注释内容:建议采用这种方式的注释
  • %注释内容%:行内注释
@echo off
Rem Rem注释在未关闭命令回显时会在屏幕显示出来,而 :: 则什么情况下都不会显示
echo 注释方式三 %行内注释%

变量

命令行参数

可以在调用批处理文件时传递参数,%n(n为自然数)表示在调用批处理文件时传递的第n个参数:

  • %0 批处理文件本身,包括完整的路径和扩展名
  • %1 第一个参数
  • %9 第九个参数
  • %* 从第一个参数开始的所有参数

参数%0具有特殊的功能,可以调用批处理自身,以达到批处理本身循环的目的,也可以复制文件自身等等。

@echo off
echo 第一个参数为:%1

copy %0 d:\new_file.bat %最简单的复制文件自身的方法%

set命令

set [/A] varName=value
:: 仅当该值是数值类型时,才可以使用【/A】,才能正确的进行数值计算,否则会被当做字符串进行计算
:: 使用变量的时候,变量需要包含在%符号中,如【%varName%】
set message=Hello World
echo %message%

SET /A a=5
SET /A b=10
SET /A c=%a% + %b%
echo %c%

set /p用于将用户输入的内容赋值给变量,以回车表示结束

set /p a=请输入姓名:
echo 姓名为:%a%
:: 先显示指定内容(不能为空),再接受用户输入的内容,最后将用户输入的内容赋值给变量

局部与全局变量

set globalvar=默认情况下都是全局变量

SETLOCAL
set var=SETLOCAL后定义的变量只在ENDLOCAL之前有效
set globalvar=SETLOCAL后重新赋的值也只在ENDLOCAL之前有效
echo %var%
echo %globalvar%
ENDLOCAL

echo %var% %会打印:【ECHO 处于关闭状态。】%
echo %globalvar%

环境变量

echo %JAVA_HOME%

判断变量是否定义

SET /A a=5
SET str=包青天

if defined a echo 【变量a存在】
if not defined b echo 【变量b不存在】
if not [%1]==[] echo 【参数存在:%1】
if exist %1 echo 【文件 %1 存在】

字符串

基本操作

set message=字符串
echo 【%message%】

set var=3
set /A var=%var% + %var%
echo 定义为字符串变量后,还可以使用【/A】开关转换为整数:%var%
:: 上面打印内容为【6】,如果不加【/A】,那么打印结果为【3 + 3】

:: 判断字符串是否相等
if [%message%]==[字符串] echo 方式一
if %message%==字符串 echo 方式二

:: 创建一个空字符串
Set a=
if [%a%]==[] echo 检查是否为空字符串

字符串截取

其实和其他语言的规则是一致的,截取的标准格式为:

%var:~num_chars_to_skip%
%var:~num_chars_to_skip,num_chars_to_keep%
即【%var:~fromIndex,length%】
SET a=abcd

echo 【%a:~0%】【%a:~1%】【%a:~2%】【%a:~3%】【%a:~4%】
:: 【abcd】【bcd】【cd】【d】【】
echo 【%a:~-4%】【%a:~-3%】【%a:~-2%】【%a:~-1%】
:: 【abcd】【bcd】【cd】【d】-n和4-n的效果是一样的

echo 【%a:~0,0%】【%a:~0,1%】【%a:~0,2%】【%a:~0,3%】【%a:~0,4%】
:: 【】【a】【ab】【abc】【abcd】
echo 【%a:~0,-4%】【%a:~0,-3%】【%a:~0,-2%】【%a:~0,-1%】
:: 【】【a】【ab】【abc】【abcd】

echo 【%a:~1,1%】【%a:~1,2%】【%a:~2,1%】【%a:~3,1%】【%a:~3,2%】
:: 【b】【bc】【c】【d】【d】

替换子字符串

%str:new=old%
set str=包青天 白  乾涛 包青天
echo 【%str:包青=老%】【%str: =_%】
:: 【老天 白  乾涛 老天】【包青天_白__乾涛_包青天】

删除子字符串

%str:subStr=%
:: 可以认为是一种特殊的替换:将指定字符替换为空
set str=包青天 白  乾涛 包青天
echo 【%str:包青=%】【%str: =%】
:: 【天 白  乾涛 天】【包青天白乾涛包青天】

if/else 语句

基本语法

  • 只有 if 语句时,if 语句的小括号是可选的
  • 有 else 语句时,if 语句必须带小括号,而 else 语句的小括号是可选的
  • if 条件不能加小括号,否则判断条件为 false
SET /A c=5
SET str=包青天

if %c%==5 echo 只有 if 语句时,if 语句的小括号是可选的
if %str%==包青天 (
    echo 如果换行的话必须加小括号,且左小括号不能换行--因为换行后就不是一条命令了,右括号不限制
    REM 注意:小括号内不能有 :: 格式的注释,可以有REM格式的注释(因为REM是命令)
)
:: 【==】既可以用来判断数值型,也可以用来判断字符串

if [%1]==[] (echo 呵呵) else echo 有 else 语句时,if 语句必须带小括号,而 else 语句小括号可选
if ([%1]==[]) (echo 呵呵) else (echo if 条件不能加小括号,否则判断条件为 false)

嵌套 if 语句

if(condition1) if (condition2) do_something
:: 只有当条件1和条件2都满足时,才会执行 do_something 块中的代码

if errorlevel

环境变量 errorlevel 的初始值为0,当一些命令执行不成功,就会返回一个数值,如:1 ,2 等。

注意:IF ERRORLEVEL 是用来测试它的上一个DOS命令的返回值的,注意只是上一个命令的返回值,而且返回值必须依照从大到小次序顺序判断。

if ERRORLEVEL nubmer commend
copy %0 new_file.bat
if errorlevel 0 echo 命令成功完成

copy %0 new/file.bat
if errorlevel 1 echo 命令失败

goto 语句

@echo off & setlocal enabledelayedexpansion
SET /A a=0

:add
set /A a+=1 & echo echo a当前的值为:%a%,a+1后的值为:!a!

if %a% LSS 3 (
    goto :add
) else (
    goto :end
)

:end
echo 执行完毕,a的值为:!a!

运算符

赋值运算符

SET /A a=3
SET /A a+=5
echo %a%
:: 其他类似的运算还有【a-=5】【a*=5】【a/=5】【a%=5】

算术运算符

SET /A a=5
SET /A b=3

SET /A c=%a%+%b%
SET /A d=%a%-%b%
SET /A e=%b%*%a%
SET /A f=%b%/%a%
SET /A g=%a%/%b%
SET /A h=%b%%%%a%
SET /A i=%a%%%%b%

echo 【%c%】【%d%】【%e%】【%f%】【%g%】【%h%】【%i%】
:: 【8】【2】【15】【0】【1】【3】【2】

关系运算符

SET /A a=5
SET /A b=10

if %a% EQU %b% echo 相等
if %a% NEQ %b% echo 不相等性
if %a% LSS %b% echo 左小于右
if %a% LEQ %b% echo 左小于等于右
if %a% GTR %b% echo 左大于右
if %a% GEQ %b% echo 左大于等于右

逻辑运算符

  • 批处理语言配备了一整套布尔逻辑运算符,如ANDORXOR,但只适用于二进制数字
  • 对于TRUEFALSE没有任何值
  • 可用于条件的唯一逻辑运算符是NOT运算符
  • 为非二进制数字实现AND/OR运算符的方法是使用嵌套的IF条件或goto语句

逻辑非

SET /A a=5
IF NOT %a%==6 echo 逻辑非运算符NOT的使用

逻辑与

借助 if 语句实现

SET /A a=5
if %a% LSS 6 (
    if %a% GTR 4 (
        echo 嵌套if实现and的功能
    )
)
if %a% LSS 6 if %a% GTR 4 echo 嵌套if实现and的功能

借助 goto 语句实现

SET /A a=5

if not %a% LSS 6 goto end
if not %a% GTR 4 goto end
:: 为了保持和上面if的结构一致,上面是用了两个not,实际肯定是可以不用not的
goto and_function

:and_function
echo 嵌套if实现and的功能
:end
echo end

逻辑或

逻辑或实现起来比逻辑与还要复杂一些,一般都需要借助 goto 语句来实现!

不借助 goto 语句实现的情况

这种方式只适合满足条件时要执行的命令比较少的场景,否则会有大量的冗余代码

SET /A a=3
IF %a% LSS 4 (
    echo 如果满足条件一,则执行n条命令
) else IF %a% GTR 6 (
    echo 否则,如果满足条件二,也会执行相同的n条命令
)
IF %a% LSS 4 (echo 满足条件) else IF %a% GTR 6 (echo 满足条件)

借助 goto 语句实现

SET /A a=5

IF %a% LSS 4 goto or_function
IF %a% GTR 6 goto or_function
goto end

:or_function
echo 嵌套if实现or的功能
:end
echo end

数组

数组类型并没有明确定义为批处理脚本中的类型,但可以模拟出来,但是有些功能使用起来会有诸多限制。

白哥说:其实批处理脚本中的数组就是多个名称类似的变量,其本身没有任何特性或语法,只不过因为这些变量的格式比较像C、Java中的数组,而数组又是那么深入人心,所以我们将其当做数组来看待了而已。

数组元素

其实就是一系列相互之间没任何关系的变量!

set a[0]=10
set a[1]=1
set a[1]=11
set a[3]=14

echo 【%a[0]%】【%a[1]%】【%a[2]%】【%a[3]%】【%a[4]%】
:: 【10】【11】【值不确定】【14】【值不确定】
:: 数组中的每个元素都需要使用set命令专门定义
:: 注意,未定义的元素的值是无法确定的,并不一定是没有值(虽然大部分情况都是没有defined、也没有值的)!

数组元素下标为变量

数组元素下标为变量时,可以认为是:一个变量的名称取决于另一个变量!

set a[0]=10
set /a k=0

:: 以下3种方式均可正确访问 a[k]
echo %k%-%a[0]%
call echo %k%-%%a[%k%]%%
setlocal enabledelayedexpansion & echo %k%-!a[%k%]!
:: 因为a[%k%]之前并没有定义(仅仅是定义了a[0]),所以必须`设置本地为延迟扩展`才能正常访问,且访问时必须使用!代替%

数组的长度

没有直接的函数来确定数组中元素的数量,只能通过遍历数组中的值列表手动计算

set d[0]=10
set d[1]=11
set x=0

:getArrayLength
if defined d[%x%] (
   call echo %x%-%%d[%x%]%%
   set /a x+=1
   goto :getArrayLength
)

echo 数组长度为 %x%
:: 注意,如果数组元素定义时不连续,这种方法计算出来的值可能就是错误的,核心在于上面的【defined】表达式

在数组中创建结构

set obj[0].Name=包青天
set obj[0].ID=66
set cur.Name=%obj[0].Name%

echo 【%obj[0]%】【%obj[0].Name%】【%cur.Name%】【%cur.ID%】
:: 【】【包青天】【包青天】【11】 cur.ID的值没有定义,所以打印结果是不确定的

函数

函数定义

定义函数的格式

:function_name 
Do_something 
EXIT /B 0
:: EXIT语句用于确保函数正常退出

调用函数的格式

Call :function_name parameter1, parameter2… parametern
:: 需要确保在主程序中放入【EXIT /B %ERRORLEVEL%】语句,以便将主程序的代码与函数分开

函数的基本用法

可以通过使用%~n(n代表参数的位置),来在函数内部访问参数

亲测,完全可以省略掉其中的~符号

set p=包青天
CALL :function_1
CALL :function_2 a p %p%
EXIT /B %ERRORLEVEL%

:function_1
echo 调用了【%0】函数
EXIT /B 0

:function_2
echo 调用了【%0】函数,参数为【%~1】【%2】【%3】,所有参数为【%*】
EXIT /B 0

函数的返回值

函数可以通过简单地传递变量名称来处理返回值,这些变量名称将在调用该函数时保存返回值

CALL :function_3 c & echo 一行代码时必须使用延迟变量【!c!】【%c%】
CALL :function_3 d
echo 不是一行代码时两种方式都可以【!d!】【%d%】

set p=包青天
CALL :function_3 %p% & echo 在函数中修改变量的值时,参数不是传入变量的引用【!p!】【%p%】
CALL :function_3 p & echo 而是传入变量的名称【!p!】【%p%】
EXIT /B %ERRORLEVEL%

:function_3
if NOT [%1]==[] set %1=baiqiantao
EXIT /B 0

参数作为输出参数时,此文件不要设置 setlocal enabledelayedexpansion,且函数内不要使用SETLOCAL、ENDLOCAL

函数中的局部变量

  • 函数中的局部变量可以用来避免名称冲突,并保持函数本地的变量变化
  • 调用SETLOCAL命令后可确保命令处理器对所有环境变量进行备份,并可在调用ENDLOCAL命令后恢复
  • 当到达批处理文件结束时,即通过调用GOTO:EOFENDLOCAL被自动调用
  • 使用SETLOCAL对变量进行本地化允许在函数中自由使用变量名称,而不必担心与函数外使用的变量名称冲突
  • 可以递归地调用一个函数,使用SETLOCAL可以确保每个级别的递归都使用自己的一组变量,即使变量名被重用
set x=Outer
set y=Outer
CALL :function_1 x y & echo 【!x!-!y!】--【%x%-%y%】
CALL :function_2 x y & echo 【!x!-!y!】--【%x%-%y%】
EXIT /B %ERRORLEVEL%

:function_1
set x=Inner1
set %2=Inner1
:: 当没有SETLOCAL时,以上两种set方式都能改变函数外的变量的值
EXIT /B 0

:function_2
SETLOCAL
set x=Inner2
set %2=Inner2
:: 当有SETLOCAL时,函数内部对变量的修改不影响函数外部的变量的值
ENDLOCAL
EXIT /B 0

2019-12-30

posted @ 2019-12-30 22:19  白乾涛  阅读(6527)  评论(0编辑  收藏  举报