Windows Batch Tips

Table of Contents

1 Batch

在Windows中,使用Batch文件可以方便地完成自动化任务,类似于Linux中的Shell脚本。Batch的完整语法可参考:Using batch files

2 Tips

2.1 for循环

FOR这条命令基本上都被用来处理文本,但还有其他一些好用的功能!

它的基本格式(注:这里我引用的是批处理中的格式,变量名前要用两个%号,直接在命令行执行则只需要一个%号):

FOR 参数 %%变量名 IN (相关文件或命令) DO 执行的命令

说明如下:
参数:FOR有4个参数 /d /l /r /f 它们的作用我在下面用例子解释;
%%变量名:这个变量名可以是小写a-z或者大写A-Z,他们区分大小写,FOR会把每个读取到的值给他;
(相关文件或命令):FOR要把什么东西读取然后赋值给变量,看下面的例子;
执行的命令:对每个变量的值要执行什么操作就写在这。

在CMD输入 for /? 可以查看系统提供的帮助。

现在开始讲每个参数的意思

2.1.1 参数/D

参数/D表示“目录”。如果Set (也就是我上面写的“相关文件或命令”) 包含通配符(* 和 ?),将对与Set相匹配的每个目录(而不是指定目录中的文件组)执行指定的Command。
系统帮助的格式: FOR /D %%variable IN (set) DO command

它主要用于目录搜索,不会搜索文件;看例子:

@echo off
for /d %%i in (*) do @echo %%i
pause

把它保存放在C盘根目录执行,就会把C盘目录下的全部目录名字打印出来,而文件名字一个也不显示。

再看一个例子,比如我们要把当前路径下文件夹的名字只有1-3个字母的打出来:

@echo off
for /d %%i in (???) do @echo %%i
pause

这样的话如果你当前目录下有目录名字只有1-3个字母的,就会显示出来,没有就不显示了。

2.1.2 参数/R

参数/R表示“递归”。
进入根目录树 [Drive:]Path,在树的每个目录中执行 for 语句。如果在/R后没有指定目录,则认为是当前目录。如果Set只是一个句点 (.),则只枚举目录树。
系统帮助的格式: FOR /R [[drive:]path] %%variable IN (set) DO command

它可以把当前或者你指定路径下的文件名字全部读取,注意是文件名字,下面是一个例子:

@echo off
for /r c:\ %%i in (*.exe) do @echo %%i
pause

它把C盘根目录,和每个目录的子目录下面全部的EXE文件都列出来了。

再看一个例子:

@echo off
for /r %%i in (*.exe) do @echo %%i
pause

参数不一样了,这个命令前面没加那个C:\也就是搜索路径,这样他就会以当前目录为搜索路径,比如你这个BAT你把他防灾d:\test目录下执行,那么他就会把D:\test目录和他下面的子目录的全部EXE文件列出来。

2.1.3 参数/L

参数/L表示“迭代数值范围”。
使用迭代变量设置起始值 (Start#),然后逐步执行一组范围的值,直到该值超过所设置的终止值 (End#)。/L 将通过对 Start# 与 End# 进行比较来执行迭代变量。如果 Start# 小于 End#,就会执行该命令。
如果迭代变量超过 End#,则命令解释程序退出此循环。还可以使用负的 Step# 以递减数值的方式逐步执行此范围内的值。例如,(1,1,5) 生成序列 1 2 3 4 5,而 (5,-1,1) 则生成序列 (5 4 3 2 1)。
系统帮助的格式: for /L %% Variable in (Start#,Step#,End#) do Command

例如:

@echo off
for /l %%i in (1,1,5) do @echo %%i
pause

保存执行看效果,他会打印从1 2 3 4 5这样5个数字,(1,1,5)这个参数也就是表示从1开始每次加1直到5终止!

再看这个例子:

@echo off
for /l %%i in (1,1,5) do start cmd
pause

执行后是不是吓了一跳,怎么多了5个CMD窗口,呵呵!如果把那个(1,1,5)改成(1,1,65535)会有什么结果,会打开65535个CMD窗口,这么多你不死机算你强!当然我们也可以把那个start cmd改成md %%i 这样就会建立指定个目录了!!!名字为1-65535。

参考:
http://stackoverflow.com/questions/2591758/batch-script-loop

2.1.4 参数/F

含有/F的for有很大的用处,在批处理中使用的最多,用法如下:

FOR /F ["options"] %%i IN (file) DO command
FOR /F ["options"] %%i IN ("string") DO command
FOR /F ["options"] %%i IN ('command') DO command

这个可能是最常用的,也是最强的命令,主要用来处理文件和一些命令的输出结果。
file代表一个或多个文件
string代表字符串
command代表命令
options可选
对于FOR /F %%i IN (file) DO command
file为文件名,按照官方的说法是,for会依次将file中的文件打开,并且在进行到下一个文件之前将每个文件读取到内存,按照每一行分成一个一个的元素,忽略空白的行,看个例子。
假如文件a.txt中有如下内容:

第1行第1列 第1行第2列 第1行第3列
第2行第1列 第2行第2列 第2行第3列
第3行第1列 第3行第2列 第3行第3列

你想显示a.txt中的内容,会用什么命令呢?当然是 type a.txt
for也可以完成同样的命令: for /f %%i in (a.txt) do echo %%i
还是先从括号执行,因为含有参数/f,所以for会先打开a.txt,然后读出a.txt里面的所有内容,把它作为一个集合,并且以每一行作为一个元素,所以会产生这样的集合:
{“第1行第1列 第1行第2列 第1行第3列”, //第一个元素
“第2行第1列 第2行第2列 第2行第3列”, //第二个元素
“第3行第1列 第3行第2列 第3行第3列”} //第三个元素

集合中只有3个元素,同样用%%i依次代替每个元素,然后执行do后面的命令。具体过程:
用%%i代替“第1行第1列 第1行第2列 第1行第3列”,执行do后面的echo %%i,显示“第1行第1列 第1行第2列第1行第3列”,
用%%i代替“第2行第1列 第2行第2列 第2行第3列”,执行echo %%i,显示“第2行第1列 第2行第2列第2行第3列”,
依次,直到每个元素都代替完为止。

为了加强理解/f的作用,请执行一下两个命令,对比即可明白:

for /f %%i in (a.txt) do echo %%i //会显示a.txt里面的内容,因为/f的作用,会读出a.txt中的内容
for %%i in (a.txt) do echo %%i    //只会显示a.txt这个名字,并不会读取其中的内容

通过上面的学习,我们发现for /f会默认以每一行来作为一个元素,但是如果我们还想把每一行再分解更小的内容,该怎么办呢?不用担心,for命令还为我们提供了更详细的参数,使我们将每一行分为更小的元素成为可能。
它们就是:delims和tokens
delims 用来告诉for每一行应该拿什么作为分隔符,默认的分隔符是空格和tab键
比如,还是上面的文件,我们执行下面的命令:

for /f "delims= " %%i in (a.txt) do echo %%i

显示的结果是:

第1行第1列
第2行第1列
第3行第1列

为什么是这样的呢。因为这里有了delims这个参数,=后面有一个空格,意思是再将每个元素以空格分割,默认是只取分割之后的第一个元素。
执行过程是:

将第一个元素“第1行第1列 第1行第2列 第1行第3列”分成三个元素:“第1行第1列” “第1行第2列” “第1行第3列”,它默认只取第一个,即“第1行第1列”,然后执行do后面的命令,依次类推。

但是这样还是有局限的,如果我们想要每一行的第二列元素,那又如何呢?
这时候,tokens跳出来说,我能做到。它的作用就是当你通过delims将每一行分为更小的元素时,由它来控制要取哪一个或哪几个。使用tokens时,会自动分配新的变量!
FOR /F "eol=; tokens=2,3* delims=, " %%i in (myfile.txt) do @echo %%i %%j %%k
echo后面的变量i,j,k是新分配的,它们对应tokens值为2,3和*(*在此表示tokens 3之后余下的所有tokens)

执行如下命令: for /f "tokens=2 delims= " %%i in (a.txt) do echo %%i ,执行结果:

第1行第2列
第2行第2列
第3行第2列

如果要显示第三列,那就换成tokens=3。
同时tokens支持通配符*,以及限定范围。
如果要显示第二列和第三列,则换成tokens=2,3或tokens=2-3,如果还有更多的则为:tokens=2-10之类的。
此时的命令为: for /f "tokens=2,3 delims= " %%i in (a.txt) do echo %%i %%j

怎么多出一个%%j?
这是因为你的tokens后面要取每一行的两列,用%%i来替换第二列,用%%j来替换第三列。
并且必须是按照英文字母顺序排列的,%%j不能换成%%k,因为i后面是j
执行结果为:

第1行第2列 第1行第3列
第2行第2列 第2行第3列
第3行第2列 第3行第3列

对以通配符*,就是把这一行全部或者这一行的剩余部分当作一个元素了。
比如: for /f "tokens=* delims= " %%i in (a.txt) do echo %%i
执行结果为:

第1行第1列 第1行第2列 第1行第3列
第2行第1列 第2行第2列 第2行第3列
第3行第1列 第3行第2列 第3行第3列

其实就跟 for /f %%i in (a.txt) do echo %%i 的执行结果是一样的。
再如: for /f "tokens=2,* delims= " %%i in (a.txt) do echo %%i %%j
执行结果为:

第1行第2列 第1行第3列
第2行第2列 第2行第3列
第3行第2列 第3行第3列

用%%i代替第二列,用%%j代替剩余的所有
最后还有skip和eol,这两个简单,skip就是要忽略文件的前多少行,而eol用来指定当一行以什么符号开始时,就忽略它。
比如: for /f "skip=2 tokens=*" %%i in (a.txt) do echo %%i
结果为:

第3行第1列 第3行第2列 第3行第3列

用skip来告诉for跳过前两行。
如果不加tokens=*的话,执行结果为:

第3行第1列

不知道怎么回事。
再如,当a.txt内容变成:

.第1行第1列 第1行第2列 第1行第3列
.第2行第1列 第2行第2列 第2行第3列
第3行第1列 第3行第2列 第3行第3列

执行 for /f "eol=. tokens=*" %%i in (a.txt) do echo %%i 的结果是:

第3行第1列 第3行第2列 第3行第3列

用eol来告诉for忽略以“.”开头的行。
同样也必须加tokens=*,否则只会显示“第3行第1列

2.2 变量替换

The syntax %var:str1=str2% takes the environment variable (or pseudo-variable in case of %TIME% and replaces str1 by str2. If nothing follows after the equals sign then str1 is simply deleted.

下面是把冒号换成连接符的例子:

C:\>echo %time%
18:52:22.20

C:\>echo %time::=-%
18-52-25.89

2.3 参数modifier

The following table lists the modifiers you can use in expansion.

Table 1: Modifiers
Modifier Description
%~1 Expands %1 and removes any surrounding quotation marks ("").
%~f1 Expands %1 to a fully qualified path name.
%~d1 Expands %1 to a drive letter.
%~p1 Expands %1 to a path.
%~n1 Expands %1 to a file name.
%~x1 Expands %1 to a file extension.
%~s1 Expanded path contains short names only.
%~a1 Expands %1 to file attributes.
%~t1 Expands %1 to date and time of file.
%~z1 Expands %1 to size of file.
%~$PATH:1 Searches the directories listed in the PATH environment variable and expands %1 to the fully qualified name of the first one found. If the environment variable name is not defined or the file is not found, this modifier expands to the empty string.

The following table lists possible combinations of modifiers and qualifiers that you can use to get compound results.

Table 2: Possible combinations of modifiers
Modifier Description
%~dp1 Expands %1 to a drive letter and path.
%~nx1 Expands %1 to a file name and extension.
%~dp$PATH:1 Searches the directories listed in the PATH environment variable for %1 and expands to the drive letter and path of the first one found.
%~ftza1 Expands %1 to a dir-like output line.

参考:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/percent.mspx?mfr=true

2.3.1 应用实例

普通变量无法使用上面这些修饰符的,它们的典型应用场景是命令行参数,函数参数,for循环中的变量。

应用举例1:分解命令行参数中的路径和文件名。

C:\DOCUME~1\jack>type 1.bat
@echo off
echo path is: %~dp1
echo file name is: %~nx1

C:\DOCUME~1\jack>1.bat C:\test\file.txt
path is: C:\test\
file name is: file.txt

应用举例2:实现Linux中的basename功能。
修饰符无法直接用于普通变量,如果一个普通变量中保存着一个带路径的文件名,怎么得到对应的不带路径的文件名呢?把变量传给一个函数即可。

C:\DOCUME~1\jack>type 2.bat
@echo off
set file1=C:\test\myfile.txt

set filename=
call :basename %file1% filename
echo file name is: %filename%

goto:eof
:basename <pathVar> <resultVar>
(
  set "%~2=%~nx1"
)

C:\DOCUME~1\jack>2.bat
file name is: myfile.txt

2.4 实现substring

描述1:
It is possible to retrieve specific characters from a string variable.

Syntax

  %variable:~num_chars_to_skip%
  %variable:~num_chars_to_skip,num_chars_to_keep%

实例:

c:\>set A=abcdef

c:\>echo $A:~2%
cdef

c:\>echo $A:~2,3%
cde

描述2:
The :~ substring command works like this:
:~[START POS],[LENGTH]

If [START_POS] is positive or zero the substring will start from the left. If the number [START_POS] is negative it will start from the right. And [LENGTH] is the number of characters in the opposite direction of the starting point.

If we wanted to get the current year we could start 4 from the end, and 4 in length. Like this:

c:\>echo %date%
Thu 10/31/2013

c:\>echo %date:~-4,4%
2013

参考:
http://www.intelliadmin.com/index.php/2007/02/create-a-date-and-time-stamp-in-your-batch-files/

2.5 输出空行

输出空行,使用命令 echo. 即可。


Author: cig01

Created: <2017-06-03 Sat 00:00>

Last updated: <2018-02-09 Fri 21:56>

Creator: Emacs 25.3.1 (Org mode 9.1.4)