批处理脚本学习笔记
原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处、作者信息和本声明。否则将追究法律责任。http://blog.csdn.net/taotaoyouarebaby/article/details/23958897
说明:本文档在LGPL开源协议下发布。本文档将批处理当作一门编程语言来看待,按编程语言的元素来组织内容。这份文档是目的不是教你各个命令的语法,而主要集中在批处理是怎样实现普通编程语言的一些功能,其中的语法说明使用的是BNF规则。文档有一定难度,不适合于连批处理是什么都不知道的情况。如果你有点编程语言基础那就更好了。完完整整的把批处理看下来,得到一个结论就是,批处理功能非常有限,很难当作一门完整的编程语言来看待!复制到网页上之后格式有点乱,所以提供PDF下载。
所有命令不区分大小写,除for的循环变量。
cmd解释器按逻辑行读取和执行。行在这是的含义:1.以回车为结束标志的一自然行。2.通过()和&&,||,&组合在一起的多个自然行。
读取一行之后,会执行以下步骤:
1) 变量替换:将参数变量(%0, %1,...,%9)和以%号引起来变量(eg: %path%)替换为实际值。
2) 去除被转义的特殊字符的语义。转义字符:""(对之间的所有元字符转义), ^(对单个字符转义)
3) 执行语法检查,生成指令序列。
4) 执行重定向
语法:
setlocal EnableDelayedExpansion | DisableDelayedExpansion set var=!var!;... ... endlocal |
功能:
1) 使用变量在逻辑行执行过程中的实际值(动态变化),而不是读取逻辑行时的值(不变,只是简单替换)。主要用于for语句。如果不启动的话,for语句中使用set命令时,多数情况下无法得到想要的结果。
2) 需要延迟绑定的变量,通过!var!的形式来获取值。支持同样的字符串操作。
语法:
setlocal EnableExtensions | DiableExtensions ... endlocal |
功能:打开一些命令的扩展功能。
注释:
rem anything :: anything. 解释器无法识别::所以会抛弃这一行,达到注释的效果。由此可以得到其它的注释方式。 |
定义:
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
|
控制输出 |
描述 |
示例 |
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 |
显示当前时间 |
|
句柄 |
等价的数字 |
描述 |
STDIN |
0 |
标准输入,即键盘输入 |
STDOUT |
1 |
标准输出 |
STDERR |
2 |
标准错误输出 |
UNDEFINED |
3-9 |
应用程序定义 |
命令 |
描述 |
> |
将标准输出重定向到文件,即将输出写入文件。以覆盖方式写入。 |
>> |
将标准输出重定向到文件,即将输出写入文件。以在尾部追加的方式写入。 |
< |
将标准输入重定向到文件,即从文件中读取输入数据。 |
| |
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命令的标准错误信息。
操作 |
描述 |
||||||||
定义/创建 |
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= |
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
显示一个变量中所有以分号分隔的字符串。
@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 |
支持的数
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: (1, 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.不支持实数(小数)运算
语法:
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 $* |
说明:下面两种方式,在当前cmd实例中对环境变量作的改变,在该实例中(及其创建的子实例中,startcmd.exe)是无法获得的。只有在下一次cmd启动时生效;重启explorer,批处理中才能生效。
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
setx说明:系统支持>=xpserver package 2。设置环境变量,永久有效。不需要重启系统。
使用方法类似一般的set命令。
setx [/M] var-name=[var-value] |
说明:
1) /M用于设置系统环境变量。
2) 只能清空,没法删除已经存在环境变量。
命令符号 |
功能描述 |
& |
a & b, 先执行a,然后执行b。 |
&& |
a && b, 先执行a,如果a执行成功(返回值为0)才会执行b. |
|| |
a || b, 先执行a,如果a执行失败(返回值非0)才会执行b. |
() |
用于将多行组合成逻辑上的一行命令。eg: ( a ) && ( b ) a,b命令虽然自在不同的行,但解释器会将其当作一行处理。变量替换时会同时替换a,b中存在的变量。 |
基本语法: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(>=)
布尔逻辑关系: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 |
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 |
在命令行下for变量使用%,在批处理中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 |
语法:
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 |
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] |
语法:
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 |
FOR %variable IN (set) DO command[command-parameters]
遍历文件。set为一个或一组文件。
FOR /D %variable IN (set) DO command[command-parameters]
匹配当前目录中的目录,不递归。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
|
如何在批处理中建立一个作用域,使用其中定义的变量在外部不可见。方法:
setlocal ...定义变量 endlocal |
怎样从外部取得setlocal...endlocal之间的值呢? setlocal ... endlocal & ( set retVal=%innerVal%) 外部就可以通过retVal访问innerVal的值。 |
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调用中退出。
call可以跟文件或标号(Label)。call 返回后会继续执行call语句之后的代码。
单独的文件 :call func.bat param1 param2 标号Label : call :func param1, param2 |
保存最近一个命令/函数的退出代码(exit...)。可以通过该变量获得上一个命令的执行状态。使用if语句判断。
未直接提供,但是可以通过简单的set进行模拟。
模拟数组:通过这种方式可以模拟出类似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 ::= 数字 | 字符串 |
创建数组:
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]! ) |
首先定义类,然后根据类创建对象。构造函数->生成并返回对象名;调用静态数据初始化函数;定义非静态数据成员。
类结构 |
实现方式 |
调用 |
构造函数 |
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 |
n 如何生成唯一的对象名?
批处理是单线程的,所以不会出现多个线程竞争的情况。cmd中可以用数字定义变量,所以对象名可以采用timestamp+random的方式,eg:201404171310269930082。并使用if defined检查是否定义。
n 如何初始化静态数据成员?
单独定义一个静态数据初始化函数。每个构造函数中检查静态数据成员是否初始化,如果没有则调用它。
n 类函数成员怎样放置?
有三种方式:
1) 在使用的位置,该类定义复制过去。使用较少时。
2) 将类成员函数放到不同的.bat文件中。可行,文件会过多。
3) 将一个类的定义都放到一个.bat文件中,通过批处理参数来区别调用的是哪个成员函数。可行,效率低点。
下面的代码,是将类定义放在使用的地方。
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 ) |
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% |
@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 |
环境准备:
1) 下载安装PHP
2) 配置path环境变量
嵌入PHP代码示例:获取MD5
@IF NOT "%~1"=="" PHP.EXE -r "print(md5('%~1'));" |
获取MD5
@IF NOT "%~1"=="" perl -MDigest::MD5=md5_hex -le "print md5_hex '%~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 |
初始化数据:表、初始化数据。 |
需求 |
技术方案 |
解压文件 |
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变量删减工具 |
|
|
建立如下的.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
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" |
将所有的文件都安装到用户指定的一个安装目录中。
安装程序目录结构:
-- │ 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版的是一样的。
@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 |
细节就省略,详细的请看代码。
n 环境变量设置不立即生效
【问题】
关于环境变量有这样一个特点:使用wmic或setx。在当前cmd实例中对环境变量作的改变,在该实例中(及其创建的子实例中,start cmd.exe)是无法获得的。只有在下一次cmd启动时生效;重启explorer,批处理中才能生效。
【解决方案】
所以在数据库创建时,需要使用命令的完整路径。
n 获取脚本所在目录
【问题】
在批处理中,需要调用当前目录下其它的批处理脚本(或其它文件)。如果直接写脚本名,即使与当前批处理在同一目录中也无效。
【解决方案】
%~dp0filename |
达到的效果:
来源: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 退出 |
说明:该工具可永久性的设置系统环境变量,不会因为退出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 |
用于从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 ) |
【描述】
报错:此时不应有 \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 ) |
环境变量改变后,必须重启explorer。否则在explorer中点击运行批处理文件时,使用的是之前的环境变量。
重启explorer: 在任务管理器中强制杀掉所有的explorer.exe进程。同样在任务管理器中,点击“文件->新建任务”,输入explorer并回车。
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版本有关吧。
%str:~start[,end]%
字符串下标从0开始。得到的字符串,
如果start==0, 为(str[start],str[end-1]);
如果start!=0, 为(str[start], str[end])。
如果字符串中有中文,就不知道它是怎么解释的了,结果完全是乱的。
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 ) |
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 |
[ |
\[ |
|
] |
\] |
|
" |
\" |
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 |
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 |
|
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 |
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 |
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 |
网络连接管理。 |
详细信息参看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 |
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的使用