批处理脚本学习笔记——程序员版


批处理脚本学习笔记

原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处、作者信息和本声明。否则将追究法律责任。http://blog.csdn.net/taotaoyouarebaby/article/details/23958897

说明:本文档在LGPL开源协议下发布。本文档将批处理当作一门编程语言来看待,按编程语言的元素来组织内容。这份文档是目的不是教你各个命令的语法,而主要集中在批处理是怎样实现普通编程语言的一些功能,其中的语法说明使用的是BNF规则。文档有一定难度,不适合于连批处理是什么都不知道的情况。如果你有点编程语言基础那就更好了。完完整整的把批处理看下来,得到一个结论就是,批处理功能非常有限,很难当作一门完整的编程语言来看待!复制到网页上之后格式有点乱,所以提供PDF下载。

1  cmd解释器基本工作原理

所有命令不区分大小写,除for的循环变量。

cmd解释器按逻辑行读取和执行。行在这是的含义:1.以回车为结束标志的一自然行。2.通过()和&&,||,&组合在一起的多个自然行。

读取一行之后,会执行以下步骤

1)  变量替换:将参数变量(%0, %1,...,%9)和以%号引起来变量(eg: %path%)替换为实际值。

2)  去除被转义的特殊字符的语义。转义字符:""(对之间的所有元字符转义), ^(对单个字符转义)

3)  执行语法检查,生成指令序列。

4)  执行重定向

 

1.1         延迟绑定技术

语法:

setlocal EnableDelayedExpansion | DisableDelayedExpansion

set var=!var!;...

...

endlocal

功能:

1)  使用变量在逻辑行执行过程中的实际值(动态变化),而不是读取逻辑行时的值(不变,只是简单替换)。主要用于for语句。如果不启动的话,for语句中使用set命令时,多数情况下无法得到想要的结果。

2)  需要延迟绑定的变量,通过!var!的形式来获取值。支持同样的字符串操作。

1.2         命令扩展

语法:

setlocal EnableExtensions | DiableExtensions

...

endlocal

功能:打开一些命令的扩展功能。

2  批处理基本语法

注释:

rem anything

:: anything. 解释器无法识别::所以会抛弃这一行,达到注释的效果。由此可以得到其它的注释方式。

 

2.1         变量

2.1.1   简单变量

定义:

set variable-name=value{value}

value := %var% | %1 | string | numbers

说明:

1) 变量名与变量值之间不能有空格

2) 变量值不能有特殊字符,如果有则需要转义

3) 变量可以重复定义。重定义时会覆盖之前定义的值。

4) 从用户输入或文件取得变量值

set /p var=提示语句

set /p var= < file.txt

 

删除:

set variable-name=

 

显示:会显示所有以variable-name为前缀的变量的值

set variable-name

 

引用:

%变量名%

 

使用示例:

@echo off

set var=value

echo %var%

pause

输出:value

 

 

2.2         输入输出

2.2.1   输出

控制输出

描述

示例

echo

输出一行信息

echo %var%    输出变量值

echo %1        输出命令行参数值

echo some-message. 输出任意信息

echo >>file.txt str 等同于:echo str >>file.txt

type

打印文本信息到标准输出

type a.txt    显示a.txt中的内容

more

逐屏显示

type a.txt | more +3

 

CLS 

清屏

路过前3行,逐屏显示a.txt内容

COLOR 

设置cmd文字与背景颜色

color 07  背景(0:黑色)文字(7:白色)

TITLE 

设置cmd标题,常当作进度条

@title welcome to GOD 进度条程序

@

隐藏命令

@echo 不显示命令

time /t

显示当前时间

 

 

2.2.2   标准输出输出句柄

句柄

等价的数字

描述

STDIN

0

标准输入,即键盘输入

STDOUT

1

标准输出

STDERR

2

标准错误输出

UNDEFINED

3-9

应用程序定义

 

2.2.3   输入输出重定向与管道命令

命令

描述

将标准输出重定向到文件,即将输出写入文件。以覆盖方式写入

>> 

将标准输出重定向到文件,即将输出写入文件。以在尾部追加的方式写入

将标准输入重定向到文件,即从文件中读取输入数据

|

a | b,将a命令的输出作为b命令的输入。名叫管道

<&

管道合并命令。a<&b 等价于 b>&a。将要输出到b的内容,输出到a。将b管的出口合并到a管的入口。

常用形式:type a.txt > b.txt 2 >& 1,将type的错误信息输出到标准输出。并将标准输出重定向到文件。

>&

常见:

1)  command > nul  等价于command 1>nul : 不显示command命令的输出信息。

eg: copy a.txt b.txt >nul.这样一来就不会显示复制完成的提示信息了。

2)  command 2>nul : 不显示command命令的标准错误信息。

 

2.3         字符串

2.3.1   常用字符串操作

操作

描述

定义/创建

set str=a;b;c;d

连接

set str=%str%otherthings

替换

set str=%str:a=c%  把str中的所有a替换为c

set str=%str:*;=%  把str中从开始到’;’为止(包含’;’)的所有内容替换为空(即删除)

剪切

%str:~start[,end]%

1)   字符串下标从0开始。得到的字符串,如果start==0, 为(str[start],str[end-1]);start!=0, 为(str[start], str[end])。

2)   start,end的取值区间:(-len, -1] & [0, len-1),负数表示从后往前数的位置。

 

str=a;b;c;d

%str:~1%    

;b;c;d

%str:~0,2% EQU %str:~1,2%

a;

%str:~1,-1%

;b;c;

清空

set str=

 

2.3.2   处理字符串中的保留字符

windowsNT的保留字符:& | ( ) < > ^。当字符串有以上字符只需要使用字符’^’对其进行转义。

一般情况:

set var=dir ^> file.txt   & rem 转义单个保留字符

set var=”dir > file.txt”  & rem 会转义双引号中的所有保留字符,但var中会含有双引号

 

 

嵌套时:

@IF NOT "%~1"=="" FOR /F "tokens=2*" %%A IN (

'REG Query HKLM\SOFTWARE\PHP /v InstallDir'

) DO (

@FOR /F %%C IN (

'%%~sB.\PHP.EXE -r "print^(md5^(\"%~1\"^)^)^;"'

) DO @SET MD5=%%C

)

说明:each level of nesting would require an extra "level" ofescaping

 

2.3.3   示例

显示一个变量中所有以分号分隔的字符串。

@echo off

set myvar=a b;c;d

set strippedvar=%myvar%

:repeat

for /f "delims=;" %%a in ("%strippedvar%") do echo %%a

set prestrippedvar=%strippedvar%

set strippedvar=%strippedvar:*;=%

if not "%prestrippedvar:;=%"=="%prestrippedvar%" goto :repeat

 

2.4         算术运算SET /A

支持的数

Octal:

SET /A Result = 020

Decimal:

SET /A Result = 16

Hexadecimal:

SET /A Result = 0x10

Or any combination:

SET /A Result = 010 + 0x20 - 24

 

算术运算汇总:对所有操作符支持复合赋值(eg: +=, -=)

Add:

SET /A Result = 12 + 4

Subtract:

SET /A Result = 23 - 7

Multiply:

SET /A Result = 8 * 2

Integer divide:

SET /A Result = 33 / 2

Modulo divide: (12)

SET /A "Result = 66 %% 25"

Shift right: (2)

SET /A "Result = 128 >> 3"

Shift left:

SET /A "Result = 1 << 4"

Bitwise AND:

SET /A "Result = 48 & 23"

Bitwise OR:

SET /A "Result = 16 | 16"

Bitwise XOR:

SET /A "Result = 31 ^ 15"

Group:

SET /A "Result = ( 24 << 1 ) & 23"

说明:

1.取模运算,在批处理中使用%%,在命令行中使用%。

2.当表达式包含特殊字符(%&<>|ˆ( or ))时,需要使用双引号引起来。

3.不支持实数(小数)运算

 

2.5         DOSKEY与命令别名

语法:

doskey macroname=[comand{$Tcommand}] 设置命令别名,等号右边不能以空格开头

doskey /MACROFILE=filename  从文件中导入命令别名设置

doskey /MACROS:ALL > filename  将所有命令别名设置导出到文件

 

Doskey宏定义的一些特殊代码:

特殊代码

含义

示例

$T

命令分隔符。允许一个宏中存在多个命令。

doskey ls=dir$Techo end

$1-$9

接收对应的批处理参数。与批处理程序中的 %1-%9 等同。

 

$*

接收别名后面的所有参数

doskey ls=dir $*

 

示例:

rem 在cmd启动时设置命令别名

cmd /k doskey /macrofile=macros.linux

doskey  myname=for /f "delims=\ tokens=2" %i in ('whoami') do @echo %i

doskey destroy=del /s /q /f $*

 

2.6         环境变量

说明:下面两种方式,在当前cmd实例中对环境变量作的改变,在该实例中(及其创建的子实例中,startcmd.exe)是无法获得的。只有在下一次cmd启动时生效;重启explorer,批处理中才能生效。

 

2.6.1   使用wmic操作环境变量

wmic是一个windows系统管理工具,功能非常强大。系统支持>=xp, >=server2003。

操作

实现: wmic environment+上下面的

create name="VarName", username="<system>", VariableValue="VarValue"

where "name='Name' and username='<system>'" delete

where "name='Name' and username='<system>'" get Name, VariableValue

where "name='Name' and username='<system>'" set VariableValue="Value"

说明:

1)  可永久性的设置系统环境变量,不会因为退出cmd而失效。设置后在下一次cmd启动时生效;重启explorer,批处理中才能生效。

2)  set功能,如果VariableValue跟的是空值,则会删除该变量。

3)  使用username="<system>",是用于设置系统环境变量的。如果去掉则是设置当前用户的环境变量。

4)  不能重复create;不能set/delete/get未创建变量。

wmic的使用参见:http://www.cnblogs.com/top5/archive/2013/06/19/3143832.html

用于设置环境变量时,为方便使用需要进行一下改造:参见永久设置系统环境变量——by WMIC

 

2.6.2   使用setx操作环境变量

setx说明:系统支持>=xpserver package 2。设置环境变量,永久有效。不需要重启系统。

使用方法类似一般的set命令。

setx [/M] var-name=[var-value]

说明

1)  /M用于设置系统环境变量。

2)  只能清空,没法删除已经存在环境变量。

3  程序流

3.1         条件执行

3.1.1   组合命令&,&&, ||, ()

命令符号

功能描述

&

a & b, 先执行a,然后执行b。

&&

a && b, 先执行a,如果a执行成功(返回值为0)才会执行b.

||

a || b, 先执行a,如果a执行失败(返回值非0)才会执行b.

()

用于将多行组合成逻辑上的一行命令。eg:

a

) && (

b

)

a,b命令虽然自在不同的行,但解释器会将其当作一行处理。变量替换时会同时替换a,b中存在的变量。

 

3.1.2   IF基本命令

基本语法:IF [NOT] condition command1[ELSEcommand2] : 如果(不)满足条件则执行command1,否则执行command2。中括号括起来的表示可选项。

IF命令

功能

1.IF [NOT] ERRORLEVEL number command ELSE command。

2.IF [NOT] %errorlevel% op number command ELSE command

检查上一个命令的返回值

1.>= n. 2. op n

IF [/I] [NOT] string1 op string2 command ELSE command

比较字符串/数字。/I不区别大小写

IF [NOT] EXIST filename command ELSE command

判断文件(夹)是否存在

IF [NOT] DEFINED variable command ELSE command

判断变量是否定义/不为空

说明:

1. ELSE逻辑上必须与IF在同一行上.

2. op可以上:EQU(==), NEQ(!=), LSS(<), LEQ(<=), GTR(>), GEQ(>=)

 

3.1.3   IF条件的布尔逻辑实现

布尔逻辑关系:a & b == !(!a | !b), a ^ b = (!a & b) | (a & !b)

IF...ELSE实现

临时变量实现

布尔算术实现

AND: %1 > 1 AND %2 <10 do command1

IF %1 GTR 1 (

     IF %2 LSS 10 (

        command1

)

)

也可写为一行:

IF %1 GTR 1 IF %2 LSS 10 command1

SET flag=1

IF NOT %1 GTR 1 SET flag =0

IF NOT %2 LSS 10 SET flag =0

IF %flag% EQU 1 command1

IF %1 GTR 1 SET cond1=1 ELSE set cond1=0

IF %2 LSS 10 SET cond2=1 ELSE set cond2=0

SET /A r = "cond1 & cond2"

IF r EQU 1 command1

OR:%1 > 1 OR %1 < 10 do command1

IF %1 GTR 1 (

   command1

) ELSE (

  IF %1 LSS 10 (

command1

)

)

SET flag=0

IF %1 GTR 1 SET flag =1

IF %1 LSS 10 SET flag =1

IF %flag% EQU 1 command1

IF %1 GTR 1 SET cond1=1 ELSE set cond1=0

IF %1 LSS 10 SET cond2=1 ELSE set cond2=0

SET /A r = "cond1 | cond2"

IF r EQU 1 command1

XOR: %1 > 1 XOR %2 > 1 do command1

只用IF...ELSE...逻辑很难实现.

SET flag = 0

IF NOT %1 GTR 1 IF %2 GTR 1 SET flag = 1

IF %1 GTR 1 IF NOT EQU 1 SET  flag = 1

IF %flag% EQU 1 echo TRUE

IF %1 GTR 1 SET cond1=1 ELSE set cond1=0

IF %2 GTR 1 SET cond2=1 ELSE set cond2=0

SET /A r = "cond1 ^ cond2"

IF r EQU 1 command1

 

3.1.4   循环实现goto,label,if

label:以冒号开始.

:start

:next

:eof

 

goto语法:

GOTO :label

说明:程序流会从当前位置跳转到label所在位置。

 

示例:卸载程序的菜单

rem 删除部分文件的菜单

:SELECT

    echo 选择需要删除的文件

    echo [1].PGSQL,PostgreSQL数据库程序

    echo [2].PGSQLData,数据库数据

    echo [3].JRE,java运行时环境

    echo [4].TOMCAT, 服务器程序

    echo [5].WEBAPP,网站主程序

    echo [6].退出

    :SELECT_AGAIN

        set /P option="输入要删除的项目[1|2|3|4|5|6]:"

        if "%option%" == "1" goto PGSQL

        if "%option%" == "2" goto PGSQLDATA

        if "%option%" == "3" goto JRE

        if "%option%" == "4" goto TOMCAT

        if "%option%" == "5" goto WEBAPP

        if "%option%" == "6" goto end

    goto SELECT_AGAIN

 

 

3.2         for循环

在命令行下for变量使用%,在批处理中for变量使用%%。

3.2.1   for变量及命令行参数扩展功能

语法:

%~[options]var-name

     options := option{option}

         option :=  | f | d | p | n | x | s | a | t | z | $PATH:

     var-name := [a-zA-Z0-9]

 

选项说明:for变量名为单个字母区分大小写。命令行参数%0~%9也可以使用这项功能。各选项可以组合使用。

功能组合

说明

示例

%~1

删除任何引号(")

%~1 : "C:" -> C:

%~f0

完整路径名:驱动器号+路径+文件名+扩展名

相当于下面四个的组合。

%~f0: D:\BatchTestDir\forexpvar.bat

%~d0

驱动器号

%~d0: D:

%~p0

路径

%~p0: \BatchTestDir\

%~n0

文件名

%~n0: forexpvar

%~x0

一个文件扩展名

%~x0: .bat

%~s0

路径,只含有短名

%~s0: D:\BATCHT~1\FOREXP~1.BAT

%~a0

文件属性

%~a0: --a------

%~t0

文件的日期/时间

%~t0: 2014/04/14 11:19

%~z0

文件的大小

%~z0: 306

%~$PATH:1

以I变量为关键字查找path变量,找到则返回第一个匹配,否则返回空。

%~$PATH:1 C:\

%~dp0

驱动器号+路径

%~dp0: D:\BatchTestDir\

 

示例代码:测试所有的变量增强功能

rem 本测试文件完整路径名:D:\BatchTestDir\forexpvar.bat

@echo off

call :show "C:"

goto :end

 

:show

echo %%~1 : %1 -^> %~1

echo %%~f0: %~f0

echo %%~d0: %~d0

echo %%~p0: %~p0

echo %%~n0: %~n0

echo %%~x0: %~x0

echo %%~s0: %~s0

echo %%~a0: %~a0

echo %%~t0: %~t0

echo %%~z0: %~z0

echo %%~$PATH:1 %~$PATH:1

echo 组合

echo %%~dp0 %~dp0

echo %%~ftza0 %~ftza0

 

:end

pause

 

3.2.2   FOR /F 分析文本

语法:

FOR /F ["options"] %variable IN (

   filenames     /*用for解析文件内容。文件名不能含有通配符。eg: in (dir.txt, dir2.txt)*/

   |"string"     /*用for解析字符串。字符串中不能含有双引号,否则cmd会解析出错。*/

   |’command’    /*用for解析命令产生的数据*/

) DO command [cmd-params]

说明:

1)  按行解析文本数据。每行解析得到的数据会依次分配给for变量。

2)  options:

eol=c           - end of line. char.指一个行注释字符的结尾(就一个)

skip=n          - skip lines count. 指在文件开始时忽略的行数。

delims=xxx      - 指分隔符集。默认为:空格和跳格键。

tokens=x,y,m-n,*  - 指每行的哪些符号被传递到每个迭代的for变量。m-n表示范围,*:表示额外的变量将在最后一个符号解析之后分配并接受行的其余文本。

usebackq        - 允许在 filenames中使用双引号扩起文件名称。

 

示例:

1.分行打印path变量中的每个路径

@echo off

set str=%path%

:next

    for /f "delims=;" %%i in ("%str%") do @echo %%i

    set prestr=%str%

    set str=%str:*;=%   & rem 删除已经显示的路径

    if not "%prestr%"=="%prestr:;=%" goto :next

:end

pause

 

3.2.3   FOR /L,标准for循环

FOR /L %variable IN (start,step,end) DO command[command-parameters]

说明:类似于C语言中的for循环:for(i= start, i <= end; i+=step)...。start,end,step都可以使用变量。

set /p size=输入循环次数:

for /l %i in (1, 1, %size%) do @echo %i

 

无限循环:

FOR /L %A IN (0,0,0) DO command [command-parameters]

 

3.2.4   FOR /R 递归目录匹配文件

语法:

FOR /R [[drive:]path]|. %variable IN (set) DO command [cmd-params]

set := keyword{,keyword}

keyword := 普通字符串 | 通配符(*, ?)

说明:

1)  递归指定的根目录(可以用’.’表示当前目录),按set匹配文件名。将匹配到的文件的完整路径传递给for变量。

2)  set示例: *.avi(所有avi格式的文件), *learn*.txt(文件名包含learn的文本文件)。

示例:生成播放列表

@echo off

rem create play list.

echo 开始以当前目录为根目录,递归地生成播放列表。

setlocal EnableDelayedExpansion & rem for中使用set进行算术运算,需要启用变量延迟绑定

set file=playlist.kpl

set n=0

echo [playlist]>%file%

for /R . %%i in (*.mp4, *.avi, *.rmvb, *.rm, *.mp3,*.ape) do (

    set /A n+=1

    echo File!n!=%%i >> %file%     & rem 文件完整路径

    echo Title!n!=%%~ni >> %file% & rem 文件名

    echo Length!n!=0 >> %file%

    echo Played!n!=0 >> %file% 

)

echo NumberOfEntries=%n% >> %file%

echo Version=2 >> %file%

echo CurrentIndex=47 >> %file%

endlocal

 

3.2.5   其它FOR功能

FOR %variable IN (set) DO command[command-parameters]

遍历文件。set为一个或一组文件。

 

FOR /D %variable IN (set) DO command[command-parameters]

匹配当前目录中的目录,不递归。set中可以使用通配符。

 

3.2.6   for中使用set与延迟绑定

for中set命令失效的问题:

SET VAR=str

FOR /F "tokens=1-3 delims=;" %%i IN ("a;b;c") DO (

    SET VAR=%VAR%;%%i

    SET VAR=%VAR%;%%j

    SET VAR=%VAR%;%%k

)

echo %VAR%

结果:

;c

期待值:str;a;b;c

原因:VAR变量在for语句之前已经定义,所以cmd在解释for语句时,会使用VAR变量的值替换for语句中的%VAR%,然后再执行for命令。

实际执行的set语句是:

set VAR=str;%%i

set VAR=str;%%j

set VAR=str;%%k

 

解决方案

方案一:临时

只要在for语句之前变量VAR没有定义,就会使用VAR的动态值。

 

方案二:变量延迟绑定技术

执行for语句前不会进行变量替换,直接使用变量在for语句执行过程中的值。

setlocal EnableDelayedExpansion + !var!

setlocal EnableDelayedExpansion

SET VAR=str

FOR /F "tokens=1-3 delims=;" %%i IN ("a;b;c") DO (

    SET VAR=!VAR!;%%i

    SET VAR=!VAR!;%%j

    SET VAR=!VAR!;%%k

)

echo %VAR%

endlocal

结果:

str;a;b;c

 

 

 

3.3         函数

3.3.1   作用域

如何在批处理中建立一个作用域,使用其中定义的变量在外部不可见。方法:

setlocal

...定义变量

endlocal

怎样从外部取得setlocal...endlocal之间的值呢?

setlocal

...

endlocal & ( set retVal=%innerVal%)

外部就可以通过retVal访问innerVal的值。

 

3.3.2   定义

rem func.bat

:func

SETLOCAL EnableDelayedExpansion

set result1=%~1   & rem 获取函数参数

set result2=%~2

ENDLOCAL & (

  rem 设置返回值

  SET RESULT1=%RESULT1%

  SET RESULT2=%RESULT2%

  exit /B 0    &  rem 退出当前批处理脚本,返回函数执行状态

)

 

退出函数:

exit /B 0

exit /B用于退出当前批处理脚本;从call调用中退出。

 

3.3.3   调用call

call可以跟文件或标号(Label)。call 返回后会继续执行call语句之后的代码。

单独的文件 :call func.bat param1 param2

标号Label : call :func param1, param2

 

3.3.4   函数返回代码%errorlevel%

保存最近一个命令/函数的退出代码(exit...)。可以通过该变量获得上一个命令的执行状态。使用if语句判断。

4  扩展批处理功能

4.1         模拟数组

未直接提供,但是可以通过简单的set进行模拟。

4.1.1   设计

模拟数组:通过这种方式可以模拟出类似PHP中的关联数组。

定义:set ArrayName[%index%]=value, set ArrayName.length=%length%

取值:取单个值:%ArrayName[1]%, 遍历:!ArrayName[%index%]!

修改:set ArrayName[%index%]=newVal

添加:set ArrayName[%index%]=value, set /A ArrayName.length+=1

删除:set ArrayName[%index%]=   set /A ArrayName.length-=1

 

其中index ::= 数字 | 字符串

 

4.1.2   实现

创建数组:

rem CreateArray.bat

rem CALL CreateArray name 10 0

:CreateArray

    set idx=0

    set name=%~1

    set len=%~2

    set initVal=%~3

    for /L %%i IN (1, 1, %len%) do (

        set %name%[%%i]=%initVal%

    )

    set %name%.length=%len%

exit /B 0    

 

遍历数组:

@echo off

call :CreateArray names 10 1

setlocal EnableDelayedExpansion

    set idx=1

    :loopstart

if %idx% GTR %names.length% (

  goto :loopend

)

    echo !names[%idx%]!

    set /a idx+=1 & goto :loopstart

:loopend

endlocal

 

说明:使用for /L遍历会失败,原因不知。代码如下:

for /L %%i IN (1, 1, %names.length%) do (

    echo !names[%%i]!

)

 

4.2         模拟对象

4.2.1   对象模型设计

首先定义类,然后根据类创建对象。构造函数->生成并返回对象名;调用静态数据初始化函数;定义非静态数据成员。

类结构

实现方式

调用

构造函数

ClassConstructor [construct-params]

功能:生成唯一的对象名并返回,生成对象非静态数据成员。

call :Constructor-Label

非静态数据成员

ObjectName.FieldName

!%ObjectName%.FieldName!

静态数据成员

ClassName.FieldName

%ClassName.FieldName%

非静态成员函数

ClassName.MethodName

call :CName.MName %objname%

静态成员函数

ClassName.MethodName

call : ClassName.MethodName

 

4.2.2   设计问题

n  如何生成唯一的对象名

批处理是单线程的,所以不会出现多个线程竞争的情况。cmd中可以用数字定义变量,所以对象名可以采用timestamp+random的方式,eg:201404171310269930082。并使用if defined检查是否定义。

 

n  如何初始化静态数据成员?

单独定义一个静态数据初始化函数。每个构造函数检查静态数据成员是否初始化,如果没有则调用它。

 

n  类函数成员怎样放置

有三种方式:

1)  在使用的位置,该类定义复制过去。使用较少时。

2)  将类成员函数放到不同的.bat文件中。可行,文件会过多。

3)  将一个类的定义都放到一个.bat文件中,通过批处理参数来区别调用的是哪个成员函数。可行,效率低点。

4.2.3   实现

下面的代码,是将类定义放在使用的地方。

4.2.3.1     对象名生成器timestamp+random

rem Utility: name allocator of class.

rem return: number string of timestamp+random. eg: 201404171310269930082

:NameAlloc

setlocal

   :loopstart

   rem 2014/04/17 周四.注意delims后面有一个空格,用于断开“周四”

   for /F "tokens=1,2,3 delims=/" %%i IN ("%date%") do set timestamp=%%i%%j%%k

   rem 14:26:24.55

for /F "tokens=1,2,3,4 delims=:." %%i IN ("%time%") do (

 set timestamp=%timestamp%%%i%%j%%k%%l%random%

   )

   if defined %timestamp% goto loopstart & 变量已定义,重新生成。

endlocal & (

    set NameAlloc.result=%timestamp%

    exit /B 0

)

 

4.2.3.2     类定义

rem class: Man

rem ==============constructors of Man==============

rem Man's constructor.

rem RETURN: by Man.ObjBuilder.result

rem         the name of Man's new instance.

:Man.ObjBuilder

    if not defined Man.load call :Man.Static & rem initialize static member.

    rem update static member.

    set /A Man.count+=1

    rem deifne nonstatic member of class.

    call :NameAlloc

    set %NameAlloc.result%.age=10

    set %NameAlloc.result%.name=zt

    set %NameAlloc.result%.work=writer

    rem return the name of new object.

    set Man.obj=%NameAlloc.result%

    exit /B 0

 

rem function: define static member of Man.

:Man.Static

    rem 类加载标志

    set Man.load=1

    set Man.count=0

    exit /B 0

 

rem ===============Member function of Man==========

rem non-static, %1 objname

:Man.isWriter

setlocal EnableDelayedExpansion

    set obj=%~1

    if !%obj%.work!==writer endlocal & exit /B %TRUE%

endlocal & exit /B %FALSE%

 

rem static function

:Man.showCount

    echo showCount:%Man.count%

    exit /B %TRUE%

 

4.2.3.3     类的使用

 

@echo off

set TRUE=0

set FALSE=1

 

setlocal EnableDelayedExpansion

    call :Man.ObjBuilder & rem call constructor

    call :Man.ObjBuilder

    echo %Man.obj% & rem object name.

    echo Count:%Man.count% & rem refer static member

    call :Man.showCount & rem call static member function.

    rem call non-static member function.

    (call :Man.isWriter %Man.obj%) && (echo is writer) || (echo not writer)

    rem show Man's non-static member.  

    echo Age:!%Man.obj%.age!, Name:!%Man.obj%.name!,work:!%Man.obj%.work!

    pause

endlocal

goto :eof

 

4.3         嵌入PHP代码

环境准备:

1)  下载安装PHP

2)  配置path环境变量

 

嵌入PHP代码示例:获取MD5

@IF NOT "%~1"=="" PHP.EXE -r "print(md5('%~1'));"

 

4.4         嵌入PERL代码

获取MD5

@IF NOT "%~1"=="" perl -MDigest::MD5=md5_hex -le "print md5_hex '%~1'"

 

5  批处理任务实战

5.1         网站部署

5.1.1   需求说明

需求:

1)  建立快捷方式以启动tomcat。

2)  建立卸载程序。

3)  支持操作系统:windows, >=xp,>= server2003, x86, x64。

4)  安装路径可配置。

需要安装的文件

说明

JRE.7z

java运行时环境,手动安装包。需要配置好JAVA_HOME,JRE_HOME,CLASSPATH等环境变量.

TOMCAT.zip

需要在conf/server.xml中配置好网站路径<Context />。

PGSQL.zip

数据库程序,手动安装包。解压会需要创建用户、数据库,并导入初始化数据;配置path。

WEBAPP.7z

解压即可

AllInOne.sql

初始化数据:表、初始化数据。

 

5.1.2   需要的工具与技术

需求

技术方案

解压文件

7z.exe, 7z.dll,32位和64位版。

创建快捷方式的工具

1.  直接使用cmd创建。过于麻烦。

2.  使用VB脚本创建。更好。

3.  桌面位置:%USERPROFILE%\Desktop

设置环境变量

wmic,setx。优先选用wmic,因为xp中只有sp2中才有。需要使用.

识别操作系统

环境变量:%PROCESSOR_ARCHITECTURE%

可能的值: x86, x64, amd64, ia64, x86_amd64, x86_ia64

清除设置的path变量

解析path变量的工具。path变量删减工具

 

 

 

5.1.2.1     通过.inf文件创建快捷方式

建立如下的.inf文件即可:

[AddLink]

setup.ini, progman.groups,, "group0=%ShortName%"

setup.ini, group0,, ""%ShortName%""

setup.ini, group0,, """%icon1name%"",""%49002%\jscript5.chm"",,0,"

 

具体参考:

http://www.robvanderwoude.com/amb_shortcuts.php

http://www.robvanderwoude.com/amb_shortcutsnt.php

http://www.robvanderwoude.com/shortcutinf.php

http://msdn.microsoft.com/en-us/library/ff549520.aspxOverview of INF Files

 

5.1.2.2     通过VB脚本创建快捷方式

shortcut.vbs:

set WshShell = WScript.CreateObject("WScript.Shell" )

set oShellLink = WshShell.CreateShortcut(WScript.Arguments.Named("shortcut") & ".lnk")

oShellLink.TargetPath = WScript.Arguments.Named("target")

oShellLink.WindowStyle = "1"

oShellLink.Arguments=WScript.Arguments.Named("args")

oShellLink.IconLocation=WScript.Arguments.Named("icon")

oShellLink.WorkingDirectory=WScript.Arguments.Named("wd")

oShellLink.HotKey=WScript.Arguments.Named("hotkey")

oShellLink.Save

参考:

http://stackoverflow.com/questions/346107/creating-a-shortcut-for-a-exe-from-a-batch-file

http://stackoverflow.com/questions/346107/creating-a-shortcut-for-a-exe-from-a-batch-file

 

使用示例:

    call %~dp0shortcut.vbs /target:"%ComSpec%" /args:"/c %~1\TOMCAT\bin\startup.bat" /shortcut:"%UserProfile%\Desktop\含能材料管理分析系统" /icon:"%~1\WEBAPP\favicon.ico" /wd:"%~1\TOMCAT\bin" /hotkey:"CTRL+SHIFT+F"

 

 

5.1.3   设计实现

将所有的文件都安装到用户指定的一个安装目录中。

安装程序目录结构:

--

│  install.bat  根据%PROCESSOR_ARCHITECTURE%跳转到合适的安装文件。

│  Readme.txt

│  unstall.bat

└─x86

    │  install.bat

    │  location.ini 安装目录

    │  unstall.bat

    ├─bin

    │      7z.dll

    │      7z.exe

    │      delstr.bat

    │      server.xml

    │      shortcut.vbs

    │      wmicenv.bat

    └─data

            AllInOne.sql

            dependency.txt

            JRE.7z

            PGSQL.zip

            TOMCAT.zip

            WEBAPP.7z

├─x64

│  │  install.bat

│  │  location.ini

│  │  unstall.bat

│  ├─bin

│  │      7z.dll

│  │      7z.exe

│  │      delstr.bat

│  │      server.xml

│  │      shortcut.vbs

│  │      wmicenv.bat

│  └─data

│          AllInOne.sql

│          dependency.txt

│          JRE.7z

│          PGSQL.zip

│          TOMCAT.zip

│          WEBAPP.7z

下面的代码以x86为例,x64版的是一样的。

5.1.3.1     安装脚本主流程

@echo off

set /p loc=<%~dp0location.ini

if {%loc%}=={} (

    echo 请先在location.ini中设置安装目录。

    goto :install_finish

)

if not exist "%loc%" (

    md "%loc%" || goto install_finish

)

 

(

    call :check_files & rem 检查需要安装的文件是否齐全。if exist

) && (

    call :check_already_installed "%loc%"

) && (

    call :welcome_info

) && (

    call :set_env "%loc%"  & rem call wmicenv.bat

    pause

) && (

    call :copy_files "%loc%" & rem 将文件解压到安装目录

) && (

    call :create_shortcut "%loc%"

    call :config_pgsql "%loc%"

) && (

    call :initialize_database "%loc%"

    call :start_tomcat "%loc%"

)

 

goto :install_finish

细节就省略,详细的请看代码。

 

5.1.3.2     问题与解决方案

n  环境变量设置不立即生效

【问题】

关于环境变量有这样一个特点:使用wmic或setx。在当前cmd实例中对环境变量作的改变,在该实例中(及其创建的子实例中,start cmd.exe)是无法获得的。只有在下一次cmd启动时生效;重启explorer,批处理中才能生效。

【解决方案】

所以在数据库创建时,需要使用命令的完整路径。

 

n  获取脚本所在目录

【问题】

在批处理中,需要调用当前目录下其它的批处理脚本(或其它文件)。如果直接写脚本名,即使与当前批处理在同一目录中也无效。

【解决方案】

%~dp0filename

 

6  附录

6.1         批处理代码

6.1.1    进度条显示工具

达到的效果:

来源:http://www.robvanderwoude.com/3rdpartybatchfiles.php#ProgressMeter

@ECHO OFF

:: Input: %1 must contain the current progress (0-100)

:: Return: None

SETLOCAL ENABLEDELAYEDEXPANSION

SET ProgressPercent=%1

SET /A NumBars=%ProgressPercent%/2

SET /A NumSpaces=50-%NumBars%

 

:: 清空之前的内容

SET Meter=

:: Note:第二FOR的最后有一个空格

FOR /L %%A IN (%NumBars%,-1,1) DO SET Meter=!Meter!I

FOR /L %%A IN (%NumSpaces%,-1,1) DO SET Meter=!Meter!

 

:: Display the progress meter in the title bar and return

TITLE Progress:  [%Meter%]  %ProgressPercent%%%

ENDLOCAL & exit /B 0  & rem 退出

6.1.2   WMIC环境变量管理工具

说明:该工具可永久性的设置系统环境变量,不会因为退出cmd而失效。设置后在下一次cmd启动时生效,不需要重启操作系统。

rem @echo off

rem wmicenv.bat var-name [var-value]

rem Operate (system) environment permanently from cmd using wmic.exe

rem 参数:%1:name %2:value

setlocal

if "%~1"=="" goto wmicenv_usage

if "%~2"=="" goto delaction ELSE goto setaction

 

:setaction

    if not defined %~1 (

        wmic environment create name="%~1", username="<system>", variablevalue="%~2" || exit /B 1

    ) else (

        wmic environment where "name='%~1' and username='<system>'" set variablevalue="%~2" || exit /B 1

    )

    exit /B 0

 

:delaction

    if not defined %~1 (

        exit /B 0

    ) else (

        wmic environment where "name='%~1' and username='<system>'" delete || exit /B 1

    )

    exit /B 0

:wmicenv_usage

    echo %~n0的正确使用方式:

    echo 设置系统环境变量:%~n0 name value

    echo 删除系统环境变量:%~n0 name

exit /B 1

endlocal

 

6.1.3    path变量删减工具

用于从path变量中删除一个指定条目。其实也不限于path变量,其它以’;’分隔的变量都可以。如果将for中的delims参数化,则更加灵活。

rem 功能:在一个以分号分隔的字符串集合中,删除所有等于给定字符串的元素.

rem 调用方法:call delstr.bat string-collection-seperate-by-semicolon string-delete

rem 注意: 1.源字符串以;分隔.

rem 通过变量delstr_retVal返回:失败为-1; 成功则为处理好的字符串(double-quoted)。

setlocal

    rem 删除参数的所有双引号, 检查是否为空,以及是否为之前调用失败返回的结果

    set strippedStr="%~1"

    if %strippedStr%=="" if %strippedStr%=="-1"  exit /B 1

    rem 获取(删除所有双引号),并检查第二参数是否为空

    set delStr="%~2"

    if %delStr%=="" exit /B 2

    rem 拆分源字符串,将不需要删除的条目通过retVal返回。

    :delstr_repeat

        for /f "delims=;" %%i in (%strippedStr%) do (

            if not %delStr%=="%%i" (

                if defined retVal (

                    set retVal="%retVal:~1,-1%;%%i"

                 ) else (

                    set retVal="%%i"

                 )

             )

        )

        rem 保存之前的值,不能直接用strippedstr进行比较。

        rem 因为最后一次循环时,strippedStr以两个双引号开头。

        rem 当它含有特殊字符时,会使得if或for失败

        set prestrippedStr=%strippedStr%

        rem 剪去从头开始到第一个;为止的所有字符,包括’;’

        set strippedStr="%strippedStr:*;=%

        if not %prestrippedStr:;=% EQU %prestrippedStr% goto :delstr_repeat

endlocal & (

    set delstr_retVal="%retval:~1,-1%"

    exit /B 0

)

 

 

6.2         Absurd

6.2.1   设置返回值时报错:此时不应有\ATI

【描述】

报错:此时不应有 \ATI

设置返回值时,set语句如果被括号括上,并且要设置的值中有’)’时就会报错。但如果set语句不被括上括上,则没有问题。

 

【代码】错误代码

setlocal

    set retval=C:\Windows;C:\Program Files (x86)\ATI

    echo end

endlocal & (

    set delstr_retVal=%retval:*;=%

)

 

【原因】

cmd解释器,首先将变量%retval:*;=%扩展为实际值,然后才分析下面这条被扩展后的语句:

endlocal & (

    set delstr_retVal= C:\Program Files (x86)\ATI

)

因为C:\Program Files (x86)\ATI中有个右括号,把后面的字符串截断。这就使得黄色部分成为了一句,而后面的\ATI成了非法的字符串。

 

【正确代码】

方法一:

endlocal & set delstr_retVal=%retval:*;=%

方法二:更好

endlocal & (

    set delstr_retVal="%retval:*;=% "

)

第二种做法更好,因为如果% retval %中有&,&&,||,<,>,>&,<&之类的命令符号时,第一种方法就会出错。

 

【最佳实践】

字符串处理过程中(入参、变量值、返回值、if判断,for循环,...)都需要保证使用双引号括起来。也可以在使用字符串之前做转义处理。需要转义的字符。

 

 

删除以分号分隔的字符串集合中的匹配字符串

setlocal

    rem 删除参数的所有双引号, 检查是否为空,以及是否为之前调用失败返回的结果

    set strippedStr="%~1"

    if %strippedStr%=="" if %strippedStr%=="-1"  exit /B 1

    rem 获取(删除所有双引号),并检查第二参数是否为空

    set delStr="%~2"

    if %delStr%=="" exit /B 2

    rem 拆分源字符串,将不需要删除的条目通过retVal返回。

    :delstr_repeat

        for /f "delims=;" %%i in (%strippedStr%) do (

            if not %delStr%=="%%i" (

                if defined retVal (

                    set retVal="%retVal:~1,-1%;%%i"

                 ) else (

                    set retVal="%%i"

                 )

             )

        )

        rem 保存之前的值

        set prestrippedStr=%strippedStr%

        rem 剪去从头开始到第一个;为止的所有字符,包括;

        set strippedStr="%strippedStr:*;=%

        if not %prestrippedStr:;=% EQU %prestrippedStr% goto :delstr_repeat

endlocal & (

    set delstr_retVal="%retval:~1,-1%"

    exit /B 0

)

 

6.2.2   explorer(资源管理器)必须重启,才能获得最新的环境变量

环境变量改变后,必须重启explorer。否则在explorer中点击运行批处理文件时,使用的是之前的环境变量。

重启explorer: 在任务管理器中强制杀掉所有的explorer.exe进程。同样在任务管理器中,点击“文件->新建任务”,输入explorer并回车。

 

6.2.3   命令被注释:REM注释逻辑行

The Windows NT Command Shell:

http://technet.microsoft.com/library/cc750982.aspx#XSLTsection128121120120

根据这份文档的描述,下面这行代码有问题:因为cmd是按逻辑行解释的,而rem是逻辑行注释,所以会将后面的所有代码注释掉。

逻辑含义

cmd实际解释

@echo off

set x=ABC

if "%X%"=="ABC" (

     rem illegal comment!

     echo "yes"

)

if "ABC"=="ABC" (rem illegal comment! echo "yes")

但经过实际测试(win7x64),没有文档上描述的问题。可能是和windows版本有关吧。


 

6.2.4   字符串截取规则不一致

%str:~start[,end]%

字符串下标从0开始。得到的字符串,

如果start==0, 为(str[start],str[end-1]);

如果start!=0, 为(str[start], str[end])。

 

如果字符串中有中文,就不知道它是怎么解释的了,结果完全是乱的。

 

6.2.5   set中的空格会成为变量值

set var-name=var-value

1)  在等号两边不能有空格,否则var-name会被置为空。

2)  var-value之后也不能有空格,否则空格会被当作变量值。因此设置返回值时,就不能使用endlocal & set var=value & exit /B 0这样的语句。而就用括号,然后换行写。

endlocal & (

    set NameAlloc.result=%timestamp%

    exit /B 0

)

 

6.3         命令参考

6.3.1   Escape Characters

windowsNT的保留字符:& | ( ) < > ^

以上字符如果不是在双引号中,主需要进行转义。

Escape Characters

待转义字符

转义方式

说明

%

%%

May not always be required in doublequoted strings, just try

^

^^

May not always be required in doublequoted strings, but it won't hurt

&

^&

^<

^>

|

^|

'

^'

Required only in the FOR /F "subject" (i.e. between the parenthesis), unless backq is used

`

^`

Required only in the FOR /F "subject" (i.e. between the parenthesis), if backq is used

,

^,

Required only in the FOR /F "subject" (i.e. between the parenthesis), even in doublequoted strings

;

^;

=

^=

(

^(

)

^)

!

^^!

Required only when delayed variable expansion is active

\

\\

Required only in the regex pattern of FINDSTR

[

\[

]

\]

"

\"

 

6.3.2   Color Code

color 背景色文字色

Code

Color

Code

Color

0

Black

8

Gray

1

Blue

9

Light Blue

2

Green

A

Light Green

3

Aqua浅绿色

B

Light Aqua

4

Red

C

Light Red

5

Purple紫红色

D

Light Purple

6

Yellow

E

Light Yellow

7

White

F

Bright White

 

6.3.3   Win NT内部命令

ASSOC

CALL

CHDIR/CD

CLS

COLOR

COPY

DATE

DIR

DPATH

ECHO

ENDLOCAL

ERASE/DEL

EXIT

FOR

FTYPE

GOTO

IF

MKDIR/MD

MOVE

PATH

PAUSE

POPD

PROMPT

PUSHD

REM

RENAME/REN

RMDIR/RD

SET

SETLOCAL

SHIFT

START

TIME

TITLE

TYPE

VER

 

 

6.3.4   常见环境变量

Variable

Typical value (may vary)

%COMSPEC%

C:\Windows\System32\cmd.exe

%PATH%

Varies. Includes
C:\Windows\System32\;C:\Windows\

%USERPROFILE%

C:\Users\{username}

%ALLUSERSPROFILE%

C:\ProgramData

%APPDATA%

C:\Users\(username}\AppData\Roaming

%CommonProgramFiles%

C:\Program Files\Common Files

%COMPUTERNAME%

{computername}

%HOMEDRIVE%

C: or sometimes D:

%HOMEPATH%

\Users\{username}

%LOCALAPPDATA%

C:\Users\{username}\AppData\Local

%PATHEXT%

.COM; .EXE; .BAT; .CMD; .VBS; .VBE;
.JS ; .WSF; .WSH; .MSC

%ProgramData%

C:\ProgramData

%PROGRAMFILES%

Directory containing program files, usually C:\Program Files

%ProgramFiles(x86)%

In 64-bit systems, directory containing
32-bit programs. Usually C:\Program Files (x86)

%PROMPT%

Code for current command prompt format. Code is usually $P$G

%Public%

C:\Users\Public

%SYSTEMDRIVE%

The drive containing the Windows  root directory, usually C:

%SYSTEMROOT%

The Windows root directory, usually C:\Windows

%TEMP% and %TMP%

C:\Users\{Username}
\AppData\Local\Temp

%USERNAME%

{username}

%WINDIR%

Usually C:\Windows

 

6.3.5   动态环境变量dynamicenvironment variables

Variable

Value

%DATE%

Current date in the format determined by the Datecommand

%TIME%

Current time in the format determined by the Timecommand

%CD%

Current directory with its full path

%ERRORLEVEL%

Number defining exit status of a previous command or program

%RANDOM%

Random number between 0 and 32767

 

6.3.6   WMIC其它功简介

WMIC语法:

WMIC [alias] [where-clause] verbs

     alias          ::= 见下面列表

     where-clause  ::= where "property=’value’ {and|or property=’value’}"

          property   ::= 不同的alias有不同的property。eg:环境变量有name, username...

     verbs          ::= create|delete|set|get|list...不同的功能支持的verbs不一样。

 

部分alias列表简介:wmic有丰富的管理功能,下面只列举了部分。

alias

功能描述,支持的verbs(不全)

PROCESS

进程管理。call create / call terminate / delete / get / list

DATEFILE

文件管理。list/get/call delete/call copy/call rename

FSDIR

目录管理。list/get/call delete/call rename

DESKTOPMONITOR 

屏幕管理。get ScreenHeight,ScreenWidth

ENVIRONMENT

环境变量。create/set/delete/get/list

SERVICE

服务管理。call StartService|StopService|delete / set

USERACCOUNT

用户帐户管理。set/rename

NETUSER

网络连接管理。

 

6.3.7   注册表管理:REG简介

详细信息参看reg帮助。

一般形式:

REG Operation KeyName arguments

     Operation ::= 见下面的表格

     KeyName::=[\\Machine\]FullKey   ;eg: HKCU\Control Panel\International

FullKey::=ROOTKEY\SubKey

             ROOTKEY ::= [ HKLM | HKCU | HKCR | HKU | HKCC ]

             SubKey  ::= 在选择的 ROOTKEY 下的注册表项的全名

KeyName示例:

HKEY_CURRENT_USER\Control Panel\International

对应的KeyName:HKCU\ControlPanel\International

 

Operation功能简介:

Operation

arguments参数简介

QUERY

/v [valueName] 查某一项值; /s 递归子项和值;

/f value-pattern 搜索数据; /t指定查找的值类型

eg:REG QUERY "HKCU\Control Panel\International" /v sCountry 查国家信息

ADD

/v [ValueName] 值名;/t type 值类型;/d data 值的内容

eg: REG ADD HKLM\Software\MyCo /v pswd /t REG_BINARY /d fe340ead

DELETE

/v ValueName(指定值) | /ve(空名值) | /va(所有值)

/f 强行删除,不提示

eg: REG DELETE \\ZODIAC\HKLM\Software\MyCo /v MTU

删除 ZODIAC 上 MyCo 下的注册表项 MTU

COPY

REG COPY KeyName1 KeyName2 [/s](递归复制) [/f](不提示强行复制)

eg: REG COPY HKLM\Software\MyCo\MyApp HKLM\Software\MyCo\SaveMyApp /s

批量管理

 

SAVE/RESTORE保存/还原LOAD/UNLOAD加载/卸载IMPORT/EXPORT导入/导出。

REG SAVE KeyName FileName [/y] | REG RESTORE KeyName FileName

REG LOAD|EXPORT KeyName FileName

REG UNLOAD KeyName

REG IMPORT FileName

COMPARE

REG COMPARE KeyName1 KeyName2 [/v ValueName | /ve] [Output] [/s]

eg: REG COMPARE HKLM\Software\MyCo\MyApp HKLM\Software\MyCo\SaveMyApp

FLAGS

 

值类型:

REG_SZ、REG_MULTI_SZ、REG_EXPAND_SZ 、REG_DWORD、REG_QWORD、REG_BINARY、REG_NONE

 

 

6.4         参考

http://www.robvanderwoude.com/battech.php: 这个网站非常好

http://en.wikibooks.org/wiki/Windows_Batch_Scripting

http://technet.microsoft.com/library/cc750982.aspx:Windows NT Shell Scripting, Chapter2

http://www.cnblogs.com/top5/archive/2013/06/19/3143832.htmlwmic的使用

 



你可能感兴趣的:(cmd,批处理)