批处理脚本学习笔记——程序猿版
批处理脚本学习笔记
原创作品。同意转载,转载时请务必以超链接形式标明文章原始出处、作者信息和本声明。否则将追究法律责任。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),负数表示从后往前数的位置。
|
||||||||
清空 |
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 |
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变量及命令行參数扩展功能
n 语法:
%~[options]var-name options := option{option} option := | f | d | p | n | x | s | a | t | z | $PATH: var-name := [a-zA-Z0-9] |
n 选项说明: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\ |
n 演示样例代码:測试全部的变量增强功能
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 |
%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; |
%ProgramData% |
C:\ProgramData |
%PROGRAMFILES% |
Directory containing program files, usually C:\Program Files |
%ProgramFiles(x86)% |
In 64-bit systems, directory containing |
%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} |
%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的使用