我以前一直以为bat不能像Linux的shell一样设置函数并且调用,事实上我发现这是可以的。不过由于批处理的特性是一行一行执行的,这种label的函数必须要写在文本的最后。如果写在了批处理命令的前面,bat会逐行运行里面的命令的。写在文档的最后面,并且在“函数区域”前跳过这些代码。
(顺便吐槽一下,这种函数的写法和linux的shell很不一样,shell的function中的命令必须写在命令的前面,让shell逐行运行一次,才能在后面的命令中被人调用。)
函数的写法:
::在这个例子中我试图写的一个函数起名为testfunction,它的操作就是运行一个echo命令。
::使用call命令,并且在后面写入函数名,函数名的前方写一个:代表这是一个label。
call :testfunction
::这个goto命令让批处理在运行的时候跳过所有函数的部分。
goto EOF
::从下面一行开始就是函数的部分,它们应该是bat脚本的最后部分,理论上不应该在下方再写任何的命令了。首先如同goto的命令一样,首先要先写一个lable
:testfunction
echo Test function is running.
rem exit /b 会让程序跳转到调用函数的命令处,而不是直接退出程序。
exit /b
:EOF
::以上就是示例脚本的所有内容。
当然你可以不把函数内容写在这个bat中,而是另外写一个bat文件,然后call这个bat的绝对路径,那也是可以的。为了方便我就暂时称呼我运行的脚本名字为a.bat,而调用的脚本为b.bat。
a.bat的内容是。
set a=aaa
call b.bat
echo %b%
pause
而b.bat的内容是
echo %a%
set b=bbb
在这两个脚本中a脚本和b脚本各自定义了一个变量,而没有定义另外一个脚本中定义的变量,但是echo的又互相是对方脚本中定义的变量。如果只有单独的脚本,两个脚本的echo命令是返回不了任何内容的。但是如果你运行了a脚本并且调用了b脚本,可以发现,call调用的脚本之间的变量是可以互通的。不过脚本之间的goto的label不能互通,即你不能在a脚本中goto到b的一个label中去。
而且不能不写call命令,如果不写而是直接写b脚本的路径,bat依旧可以运行,不过运行完b脚本之后,整个批处理就退出了。例如我们将a脚本中的call b.bat中的call去掉,那么脚本只会打印变量a的值,然后赋值变量b,但是不回会到a脚本再次打印b的值。
写法是:
set /p var=<c:\path\to\file.txt
这样file.txt里的值就到了var这个变量里去了,不过这个写法要求txt只有一行,如果有多行,那么也只会取第一行的内容。
那如果想要其他的行,那么这个就要用到for循环了。通过for循环可以读取文本文件(或一个命令的结果)每一行的特性,将一行的内容赋值到一个变量里,不过如果光写for循环而不使用计数器的话,那么变量的值会被反复覆盖,仅保留最后一行的值。
set num=0
for /f "tokens=1" %%i in ('wmic Logicaldisk where "Caption='C:'" get Size ^| find /v /i "size"') do (
set /a num=!num!+1
if !num!==1 (set C_size=%%i)
)
要注意的是计数器由于是不停的变化的,因此要用延时变量,用!包起来而不是%,而for循环的变量是%%连续两个百分号表示的。如果输入的命令中有管道命令|,要用转移符^转译。
for的token参数可以决定一行根据指定的分隔符(默认是空格)分割成几段,取第几段。如果你写的是表格的话,那就是第几列,而num计数器可以在if里找行。这样的话这个循环就能取出列和行中的指定数据,赋值到一个变量里。
在bat中调用vbs。
当然本身的命令是非常简单的直接call就可以了,call xxx.vbs
不过这里要说的是将bat中的一些变量的值传递到vbs中去,
例如:
在bat中
set a=testmessage
call b.vbs /messageargs:%a%
在vbs中用
set wshnamed=wscript.arguments.named
msgBox wshnamed.item("messageargs")
这样的写法来获取bat变量。
以管理员身份运行bat
%1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit