bat中函数定义及技巧总结
最近因为业余时间研究了⼀下如何写bat脚本⽂件,遇到了⼏个坑,折腾了很久。总结⼀下,已做备忘。整个命令学习搞下来,发现其实cmd help⾥⾯都有说明,东查西查其实最终都是⼿册⾥的东西,所以遇到问题先研读⼿册,切记,切记!推荐⼀个查询windows下脚本使⽤说明的不错的⽹站:
正⽂开始:
1. bat赋值语句中⾮必要位置不要使⽤空格。由于长期写c++代码造成的习惯,变量赋值的前后加空格。bat命令中仍采⽤该种写法,会命令执⾏时会出现⼀些稀奇古怪的问题,期初以为是命令使⽤不正确,拷贝⽹上实例代码仔细对照后发现是赋值符号的前后多了两个空格。如:
::错误写法,输出结果为:%var%
set var = abc
echo %var%
::正确写法,输出结果为:abc
set var=abc
dos命令运行exe程序
echo %var%
2. bat命令执⾏时,⾃动开启命令回显,需要在bat⽂件头,输⼊echo off命令;这样调⽤的命令不会有回显,仅显⽰正常输出的结果,如:
::输出为:hello
echo off
echo hello
echo.
echo.表⽰输出空⾏,echo和.号之间不能有空格。
命令与某些符号之间要有空格否则会导致解析错误,如:
rem 如下命令解析失败
for %%a in (a b c) do(echo %%a)
do和其后的括号之间要有空格。
命令调⽤中若参数包含特殊字符则要将参数⽤引号,或者解析器不能区分当前传⼊为两个参数的,也可⽤引号。如下所⽰:
rem 第⼆个参数不加引号,解析会报错不到⽂件
xcopy /Q /Y /F /S ..\plugin\trunk\target\libs\win64\*.* .\exe
rem 对第⼆个参数加引号,明确是两个⼊参;
xcopy /Q /Y /F /S "..\plugin\trunk\target\libs\win64\*.*" ".\exe"
总之多加点引号,有好处。
3. for循环的使⽤,写个简单例⼦,记住基本使⽤
for %%i in (a b c) do (echo %%i)
需要注意的是:
1) 循环变量的变量名仅能为单字母变量,且区分⼤⼩写;
2) 循环变量的引⽤做如下区分:在CMD窗⼝中为%i,在bat⽂件中为%%i;
3) 循环的变量是⾮延迟扩展的;
4. 变量的⾮延迟扩展,是说bat命令在逐⾏执⾏时,单⾏中的变量引⽤会先做替换,然后执⾏ 。如下⽰例,求集合中数字的总和:
::输出结果为:3
echo off
set /A n=0
for %%i in (1,2,3) do (
set /A n=%n%+%%i
)
echo %n%
在for语句执⾏时,由于是⾮延迟扩展,所以将语句中的%n%替换为0,所以最终输出结果为3。为能够让变量n在运⾏时展开,则可开启延迟展开,此时通过在⽂件开头调⽤setlocal语句,同时⽤!n!⽅式引⽤
变量达到延迟展开效果,⽽%n%依然是⾮延迟展开。如下输出即为正确结果:
::输出结果为:6
setlocal enabledelayedexpansion
set /A s=0
for %%i in (1,2,3) do (
set /A s=!s!+%%i
)
echo %s%
多条语句的跨多⾏编写,可⽤()包裹多⾏语句。
还有⼀种较为特殊的场景是字符中包含特殊字符,在变量引⽤中若不使⽤延迟展开则会出现不错误的结果,⽰例代码如下:
@echo off
setlocal ENABLEDELAYEDEXPANSION
set x=^> is the greater than symbol
echo %x%
echo !x!
endlocal
pause
第⼀个ehco在对命令解析时,会认为>是重定向到⽂件is,然后将后续字符输出到⽂本⽂件is中;
第⼆个则为延⾄加载效果,^>是转移序列,最终是输出">is the greater than symbol",为预期的输出结果。
所以在对⽂本字符串的处理中,为避免其中包含特殊字符导致解析错误,最好都是⽤延迟加载⽅式。
6. 经常会有使⽤某个⼯具⽽⼯具⼜未在path路径下的情况,这时输⼊命令执⾏会提⽰“不是可执⾏程序”。通过cd命令进⼊⼯具所属路径有需要来回切换路径很⿇烦。如果经常使⽤该命令可以在系统属性中
修改path变量,若只是是偶尔⽤⼀下,则可以⽤path命令是修改path 路径,这样仅修改当前cmd环境中⽣效。如:
path c:\system32;%path%
这样就可以直接访问c:\system32路径下的⼯具了。
7. 如何在bat⽂件中定义函数?在国外⼀个⽹站DosTips上到了灵感,摘抄⼀下。这个⽹站收集了⼀些常⽤的bat脚本,同时也是bat脚本问题交流的论坛,如果对bat感兴趣,可以关注⼀下。
REM 输出为:Hello World
echo off
set str1=Hello
set str2=World
set joint=
call :myStrJoint %str1%,%str2%,joint
echo %joint%
pause
REM join two strings witch space
:myStrJoint
set new_str=%~1 %~2
set %~3=%new_str%
goto:eof
如上函数中输⼊str1和str2,⽽后调⽤myStrJonit过程,拼接好的字符串保存⾄全局变量oin。其中也使⽤了⼀点字符串拼接的技巧,就是%str1% %str2%,中间的空格也会⼀并作为字符拼接到字符串中。在函数定义和使⽤上,注意以下⼏点:
a. 函数的名称即为label名称,在函数的结尾处必须有goto:eof;
b. 函数的调⽤⼀定要是call,不能为goto,若为goto则在函数执⾏结束后直接退出了。
c. ⼊参的传递⽅式,多个参数中间使⽤逗号或者空格分开,若为变量的值传递则要通过%var%对变量解引⽤,若为传递变量名称则不需要。函数调⽤中实际返回值是通过对环境变量join复制⽽做到的。
由此也引⼊⼀个疑问,如果是局部作⽤域的变量该如何返回。如下⽰例
echo off
echo testFunc
set str1=void
call:testFunc str1
echo %str1%
echo testFunc1
set str1=void
call:testFunc1 str1
echo testFunc2
set str1=void
call:testFunc2 str1
pause
REM==========================
:testFunc
SETLOCAL
set astr=DosTips
(ENDLOCAL
set %~1=%astr%
)
goto:eof
:
testFunc1
SETLOCAL
set astr=DosTips
set %~1=%astr%
ENDLOCAL
goto:eof
:testFunc2
SETLOCAL
set astr=DosTips
ENDLOCAL & set %~1=%astr%
echo %astr%
goto:eof
如上3种写法中只有testFunc是有效的其他两种均⽆效。原理上应该还是因为⾮延迟展开,即在最后⼀条语句执⾏时,对命令中的astr做了替换。testFunc2中&是告诉命令解析器,同⼀⾏中有两个命令,但由于命令执⾏还是在ENDLOCAL执⾏结束后,⽆法再访问到astr,所以未能将值拷贝到%~1。简单验证如下:
REM 输出为:1
set /A num=1
set /A num=%num%+1 & set /A num=%num%
echo %num%
由于&连接的两个命令是不跨⾏的,⽽且num为⾮延迟替换,所以最终输出结果为1,但这种⾮延迟替换效果在ENLOCAL却没有⽣效。
最后记录⼀个⼩技巧,有时会希望在⽂件管理器的特定路径下打开CMD窗⼝。当然先打开CMD窗⼝,此时为命令⼯具的默认路径,然后cd /d切换到⽬标路径。还有如下两种操作⽅式:
1) 在⽬标路径下,按住Shift键,单击右键,弹出菜单中选择“在此处打开命令窗⼝”;
2) 资源管理器的地址栏中输⼊cmd,此时命令窗⼝默认也是当前路径。
11. call set说明
call语句之后可以跟bat⽂件路径或label标识名称,调⽤批⽂件或⼦过程。但是在说明⽂档中没有提及的是call之后可以跟Set之类的cmd命令,FOR和IF命令除外。解释说明如下截图:
应该是不建议使⽤的,因为对call之后的命令解析有前置的⽂件查过程。
12. bat中的字符串处理:
a. 剔除⾸部的空格,注意字符串要有空格修饰;
set /P var=
echo The input:%var%
for /f "tokens=* delims= " %%a in ("%var%") do set var=%%a
这⾥有⼀层隐藏的含义,字符串%var%按照delims指定的间隔符做⼀次段分割,分割为多段后通过变量名称顺序匹配,如%%a引⽤对应分割后第1段,%%b对应分割后的第2段,以此类推。tokens是对当前分组内容的说明:
tokens=*,意味着不做分段,整个对应1段;
tokens=1*,拣出第1段与%%a对应。其余的部分作为第2段对应到%%b
tokens=1,2*,则捡出1和2段,其余部分对应为第段。以此类推。
分段结果对应当前循环,所以如果分段的是字符串,则只会循环⼀次,不会因为分多段,每个循环取⼀段处理。
b. 截取⼦串,Substrings are done using the :~start,length notation:
%var:~10,5%
start可以为负值。
12. 获取本机的IP地址
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论