一、本文摘要

此文主要研究对代码分支化执行和重复利用的实现。

分支化执行指根据中途的实际执行结果决定下一步执行的代码,跳转的代码行号;分支化执行大概分为跳转执行、条件判断执行;因此,分支化执行基本是只执行部分代码,部分代码不执行。

代码重复利用的实现,一方面依赖程序调用(详见本人写的CMD命令实现程序调用一文),另一方面基于循环命令。

二、跳转命令——goto

打印goto命令的帮助信息。

我们可以看到该命令的参数只有一个label。

具体应用方法——在goto命令的下方放一行,开头是英文冒号后边紧跟“分支标识符”,然后再goto所在行后面加上“分支标识符”的label参数。

上应用实例,某bat文件的代码如下:

chcp 65001
@echo off
echo 小大人,您玩累了吗?
goto part1
echo 我累了,我不想说话,麻烦转给part1先生
:part1
echo ......
echo ......
echo ......
echo 过了十分钟后...
echo 现在按任意键即可继续玩耍啦
pause

执行后,结果如图所示,【echo 我累了,我不想说话,麻烦转给part1先生】并没有被执行。

注意:对于goto命令后面的标签,既可以不加冒号(goto part1),也可以加冒号(goto :part1),效果一样。

三、错误判断分支执行——errorlevel和&&、||

3.1 错误回馈命令——errorlevel

准确来说,errorlevel是一个变量,而非命令;因此直接打印它会出错哦。

打印用什么?当然是echo小哥了,执行命令【echo %errorlevel%】。

如果结果输出的是0,那么说明没有任何错误;而错误码越大说明错误严重程度越强。

错误码的取值不可能比0还小,也即不能是负数。

3.2 错误判断操作符——&&、||

在专栏【Cmd-Dos】中,有一篇文章专门对cmd dos命令中所有的操作符(功能符号)进行了总结。

【批处理DOS-CMD命令-汇总和小结】-Cmd窗口中常用操作符

上文提到的&&和||都具有判断的功能,但是本质上和if命令语句还是有差别的,因为它们只能用来判断执行是否顺利(例如是否在目标文本文件中查找到指定字符串),或者命令语法出错,判断指标就是看前一条命令执行后变量errorlevel的值,如果值为0就表示顺利,如果不为0就算错误。

如下图所示,虽然del命令删除一个不存在的文件不算错误,但是对一个不存在的文件进行改名就属于错误了。

3.2.1 Cmd命令的通用错误导致errorlevel改变

(1)命令语法类错误

错误A:不是内部或外部命令,也不是可运行的程序

例如,在没学习本文的3.2.2小节之前,我还不知道很多条件判断关键字只能在if命令语句中使用,因此执行命令【3 GTR 5 && echo 3大于5 || echo 3小于等于5】时出现了错误。

错误B:无效开关

什么时候会报错无效开关呢?当相对路径中存在反斜杠符【\】时,而使用的命令语句仅支持解析正斜杠符【\】时,就会报错无效开关。

比如dir命令,在指定相对路径时只能用反斜杠符【\】,一旦用正斜杠【/】,就会报错无效开关。

另外一种情况也会报错无效开关。那就是当你打印某个命令的帮助信息时,问号用的是中文字符。例如执行命令【dir /?】 。

错误C:参数格式不正确

当绝对路径中存在反斜杠符【\】时,而使用的命令语句仅支持解析正斜杠符【\】时,就会报错参数格式不正确。

(2)不存在类、找不到类错误

在复制、移动、改名、查询、显示或对一个文件(夹)做出其他操作时,我们需要保证这个文件(夹)切切实实地存在,不然就会发生错误。

复制命令copy,当要复制的文件不存在,报错。

移动命令move,当移动去向找不到时,也会报错。

改名命令ren,当要改名的文件不存在,报错。

查询目录构成或指定文件位置的命令dir,找不到文件时会报错,另外没在图中显示的“指定了一个不存在的目录”

查询字符串的命令find、findstr,在搜索源中查不到目标字符串,就会报错。

 (3)编码类错误

当cmd使用默认编码(ANSI编码),而文本文件中的字符是utf8编码时;

将其从中提取至cmd窗口中就会报错——写入错误,

将ANSI编码修改成utf8编码。

(4)看似会出错实际没错

反转A:删除不存在的文件(夹)没错

3.2.2 逆向认知-无法代替if命令语句的很多功能

(1)if能判断是否存在,但是&&、||不能

if [not] exist <filename> <command> [else <expression>]

(2)if能判断两个字符串之间是否相同,但是&&、||不能

if [not] <string1>==<string2> <command> [else <expression>]

(3)if能比较两个数之间的大小(例如关系运算符EQU表示等于),但是&&、||不能。

if [/i] <string1> <compareop> <string2> <command> [else <expression>]

(4)if能判断某个变量是否被定义,但是&&、||不能

if defined <variable> <command> [else <expression>]

(5)if能判断错误码是否大于等于某个具体值,但是&&、||不能;&&、||只能判断错误码是否等于0。

if [not] ERRORLEVEL <number> <command> [else <expression>]

(6)if能判断cmd版本号是否大于等于某个具体值,但是&&、||不能

if cmdextversion <number> <command> [else <expression>]

上面的六大功能,我会在本文的第五部分展开讲解。

四、循环遍历命令for的讲解

参考来源:批处理命令——for - kaizen - 博客园

4.1 打印for命令的帮助信息

D:\D-desktop>for /?
对一组文件中的每一个文件执行某个特定命令。FOR %variable IN (set) DO command [command-parameters]%variable  指定一个单一字母可替换的参数。(set)      指定一个或一组文件。可以使用通配符。command    指定对每个文件执行的命令。command-parameters为特定命令指定参数或命令行开关。在批处理程序中使用 FOR 命令时,指定变量请使用 %%variable
而不要用 %variable。变量名称是区分大小写的,所以 %i 不同于 %I.如果启用命令扩展,则会支持下列 FOR 命令的其他格式:FOR /D %variable IN (set) DO command [command-parameters]如果集中包含通配符,则指定与目录名匹配,而不与文件名匹配。FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]检查以 [drive:]path 为根的目录树,指向每个目录中的 FOR 语句。如果在 /R 后没有指定目录规范,则使用当前目录。如果集仅为一个单点(.)字符,则枚举该目录树。FOR /L %variable IN (start,step,end) DO command [command-parameters]该集表示以增量形式从开始到结束的一个数字序列。因此,(1,1,5)将产生序列1 2 3 4 5,(5,-1,1)将产生序列(5 4 3 2 1)FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]或者,如果有 usebackq 选项:FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]fileset 为一个或多个文件名。继续到 fileset 中的下一个文件之前,每份文件都被打开、读取并经过处理。处理包括读取文件,将其分成一行行的文字,然后将每行解析成零或更多的符号。然后用已找到的符号字符串变量值调用 For 循环。以默认方式,/F 通过每个文件的每一行中分开的第一个空白符号。跳过空白行。你可通过指定可选 "options" 参数替代默认解析操作。这个带引号的字符串包括一个或多个指定不同解析选项的关键字。这些关键字为:eol=c           - 指一个行注释字符的结尾(就一个)skip=n          - 指在文件开始时忽略的行数。delims=xxx      - 指分隔符集。这个替换了空格和制表符的默认分隔符集。tokens=x,y,m-n  - 指每行的哪一个符号被传递到每个迭代的 for 本身。这会导致额外变量名称的分配。m-n格式为一个范围。通过 nth 符号指定 mth。如果符号字符串中的最后一个字符星号,那么额外的变量将在最后一个符号解析之后分配并接受行的保留文本。usebackq        - 指定新语法已在下类情况中使用:在作为命令执行一个后引号的字符串并且一个单引号字符为文字字符串命令并允许在 file-set中使用双引号扩起文件名称。某些范例可能有助:FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k会分析 myfile.txt 中的每一行,忽略以分号打头的那些行,将每行中的第二个和第三个符号传递给 for 函数体,用逗号和/或空格分隔符号。请注意,此 for 函数体的语句引用 %i 来获得第二个符号,引用 %j 来获得第三个符号,引用 %k来获得第三个符号后的所有剩余符号。对于带有空格的文件名,你需要用双引号将文件名括起来。为了用这种方式来使用双引号,还需要使用 usebackq 选项,否则,双引号会被理解成是用作定义某个要分析的字符串的。%i 在 for 语句中显式声明,%j 和 %k 是通过tokens= 选项隐式声明的。可以通过 tokens= 一行指定最多 26 个符号,只要不试图声明一个高于字母 "z" 或"Z" 的变量。请记住,FOR 变量是单一字母、分大小写和全局的变量;而且,不能同时使用超过 52 个。还可以在相邻字符串上使用 FOR /F 分析逻辑,方法是,用单引号将括号之间的 file-set 括起来。这样,该字符串会被当作一个文件中的一个单一输入行进行解析。最后,可以用 FOR /F 命令来分析命令的输出。方法是,将括号之间的 file-set 变成一个反括字符串。该字符串会被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到内存中,并被当作文件分析。如以下例子所示:FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i会枚举当前环境中的环境变量名称。另外,FOR 变量参照的替换已被增强。你现在可以使用下列
选项语法:%~I          - 删除任何引号("),扩展 %I%~fI        - 将 %I 扩展到一个完全合格的路径名%~dI        - 仅将 %I 扩展到一个驱动器号%~pI        - 仅将 %I 扩展到一个路径%~nI        - 仅将 %I 扩展到一个文件名%~xI        - 仅将 %I 扩展到一个文件扩展名%~sI        - 扩展的路径只含有短名%~aI        - 将 %I 扩展到文件的文件属性%~tI        - 将 %I 扩展到文件的日期/时间%~zI        - 将 %I 扩展到文件的大小%~$PATH:I   - 查找列在路径环境变量的目录,并将 %I 扩展到找到的第一个完全合格的名称。如果环境变量名未被定义,或者没有找到文件,此组合键会扩展到空字符串可以组合修饰符来得到多重结果:%~dpI       - 仅将 %I 扩展到一个驱动器号和路径%~nxI       - 仅将 %I 扩展到一个文件名和扩展名%~fsI       - 仅将 %I 扩展到一个带有短名的完整路径名%~dp$PATH:I - 搜索列在路径环境变量的目录,并将 %I 扩展到找到的第一个驱动器号和路径。%~ftzaI     - 将 %I 扩展到类似输出线路的 DIR在以上例子中,%I 和 PATH 可用其他有效数值代替。%~ 语法
用一个有效的 FOR 变量名终止。选取类似 %I 的大写变量名
比较易读,而且避免与不分大小写的组合键混淆。

在下面的3.1.2至3.1.4我会帮大家小结一下各个字段的基本语法规则,大家如果一下子看不懂,没关系,可以先去3.1.5至后面看看每一种参数的应用案例。

4.2  variable的基本语法规则

(1)只能是26个字母中之一,当然10个数字之一也行,但是容易和批处理形式参数%0-%9冲突,还是建议用26个字母之一。

(2)区分大小写,也即%i和%I是不一样的。

(3)在cmd窗口中使用,变量名必须用单%引用(即:%variable);在批处理脚本中使用,变量名必须用双%引用(即:%%variable)。

(4)当参数设置为/f时,如果variable设置成i,那么后面的parameters必须设置成i、j、k、l...,也即如果需要向command输入多个parameters,那么parameters变量只能依序从variable开始命名。

4.3 set的基本语法规则

(1)圆括号()省不得,也即毫无商量余地,必须写成(set)的形式。

(2)当不设置任何参数(也即不用/l、/d、/f、/r任一个)时,一个set可以包括多个元素,元素之间可用空格、跳格、逗号、分号或等号分隔。

(3)当参数设置为/l时,set只能有3个元素,元素之间只能用逗号分隔,分别是start(起始值)、step(步长值)、end(最大值,可以等于但是不能比这个更大)。

(4)当参数设置为/d时,

4.4 command和parameter的语法规则

在帮助信息里的command-parameters对应本篇文章的parameters。

(1)正常情况下parameter只有一个,并且和variable完全一致。

(2)当for命令参数为/f时,parameters可能有多个,如果variable设置成i,那么后面的parameters必须设置成i、j、k、l...,也即如果需要向command输入多个parameters,那么parameters变量只能依26字母的顺序从variable开始命名。

(3)

4.5 无参数情况下,set必须手动指定多个

在没有参数的情况下,for命令的语法规则如下所示。

在cmd窗口中使用格式。

FOR %variable IN (set) DO command [command-parameters]

在批处理脚本中使用格式。

FOR %%variable IN (set) DO command [command-parameters]

举个例子,假设我们要连续输出一串数字。

新建一个bat文件,代码如下:

@echo off
for %%i in (1 2 3 4 5) do @echo %%i
pause>nul

输出结果如下

4.6 参数为/l时,只需指定首、步长、和尾

在参数为/l时,for命令的语法规则如下所示。

在cmd窗口中使用格式。

for /l %variable in (start,step,end) do command [command-parameters]

在批处理脚本中使用格式。

for /l %%variable in (start,step,end) do command [command-parameters]

对set(start,step,end)的详细限制:

[1] start指起始值;step指步间距;end指终止值。

[2] start、step和end都只能取整数(正负皆可)。

[3] 步间距step的值不能为0。

[4] 当步间距step的值为正整数时,终止值end不能小于初始值start。

[5] 当步间距step的值为负整数时,终止值end不能大于初始值start。

举个例子,假设我们要输出【2,4,6,8,10,12,14,16,18,20 ,22,24,26,28,30】,因为太长了就不适合用本文3.1.5节的方法。

我们可以新建一个bat文件,代码如下。

@echo off
for /l %%i in (2,2,30) do echo %%i
pause>nul

输出结果如下。

当大家明白了for /l 的具体功能之后,是否会想到了与它有异曲同工之妙的goto循环语句呢? 似乎,for /l 和 goto 循环语句可以相互替换?

一般而言,for /l语句可以换成goto循环,但是,goto循环并不一定能被 for /l 语句替换掉。

具体原因,请大家仔细想想。

只是就大家非常关心的一个问题提供一个简洁的答案,那就是:什么时候该用for /l计数循环,而什么时候又该用goto条件循环?

答案非常简单:

当循环次数确定的时候,首选for /l语句,也可使用goto语句但不推荐;

当循环次数不确定的时候,用goto语句将是唯一的选择,因为,这个时候需要用if之类的条件语句来判断何时结束goto跳转。

4.7 参数为/d时,对文件(夹)进行操作

当for命令的参数为/d时,对指定目录层级的文件(夹)进行操作,语法规则如下。

在cmd窗口中。

for /d %variable in (set) do command [command-parameters]

在bat脚本文件中。

for /d %%variable in (set) do command [command-parameters]

(1)该情况的set支持通配符【*】和【?】,当使用通配符时会搜索指定驱动器和路径下的文件夹。

假设我们想在music文件夹下搜索以“test”开头的文件夹有多少。

执行命令【tree /f】,可以看到music下有3个文件夹,test1下还有一个子文件夹test4。还有一些隐藏文件夹、隐藏文件没有显示出来,但你要知道它们的名称都带有hide关键字。

bat文件代码如下所示。

@echo off
for /d %%i in (C:\Users\Administrator\Downloads\Music\test*) do echo %%i
pause>nul

执行结果如下,并不包括test1下的test5、test2下的test3,这是因为只搜索第一级文件夹;另外它也和for /r一样没办法搜索到隐藏文件夹

(2)当不使用通配符时,用法和不带任何参数的用法一样。

例如,将以下的代码改一下。

@echo off
for %%i in (1 2 3 4 5) do @echo %%i
pause>nul

加一个参数/d,也是无违事理的!

@echo off
for /d %%i in (1 2 3 4 5) do @echo %%i
pause>nul

4.8 当参数为/r时,进行递归遍历

当for命令的参数是/r时,会递归遍历指定路径及其子级目录的文件(夹),基本语法规则如下。

在cmd窗口中。

FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]

在bat脚本中。

FOR /R [[drive:]path] %%variable IN (set) DO command [command-parameters]

[[drive:]path]的用法很简单。如果不指定,就默认为当前目录。该路径参数是递归遍历的起始点,得到的路径参数只会更深,而非更浅。

(set)值得讲解,下面分两种情况。

(1)当set不带有通配符时,直接递归遍历得到指定目录及其属于其下的所有子级目录,然后将这些目录路径的后边依次与set中的各字符串元素连接得到新的路径。

测试用的文件夹及其文件,结构层次和上面参数/d的第(1)部分所述一样。

测试代码如下。

@echo off
for /r C:\Users\Administrator\Downloads\Music %%i in (a b c) do echo %%i
pause>nul

测试结果如下 ,没想到竟然会查询隐藏文件夹

(2)当set中带通配符时,递归遍历得到指定目录下的所有文件路径,只会留下文件名符合set中语法规则的路径。

同样测试的环境同(1)。

测试代码如下。

@echo off
for /r C:\Users\Administrator\Downloads\Music %%i in (test*) do echo %%i
pause>nul

执行结果如下,并没有查询隐藏文件。

4.9 参数/d和参数/r组合——递归遍历各个子级文件夹

在for命令中,(貌似)只有参数/r和参数/d可以组合使用,组合效果其实就是在参数/d上的拓展,使其能够递归遍历各个子级文件夹中符合要求的目录。

还是以(1)中的示例为基础,下面的命令能搜索Music文件夹下(包括子级文件夹)所有以test开头的目录。

@echo off
for /d /r C:\Users\Administrator\Downloads\Music %%i in (test*) do echo %%i
pause>nul

上面的命令,千万要注意/d在/r前面,不然双击执行会闪退。

/r后面的路径参数,其实代表着搜索的“起始位置”,本质上是和参数/r搭配使用的,表示递归遍历目标路径及其子级目录下的文件(夹)。

in后面的(set),代表着匹配规则,本质上是和参数/d搭配使用的,有通配符时表示匹配符合规则的文件夹。

综合起来,就是只遍历在路径【C:\Users\Administrator\Downloads\Music】中(包括子级目录)符合规则【test*】的文件夹。

双击执行效果如下。完美符合预期!

这两个参数组合在一起,弥补了只用参数/d不能递归遍历子级目录的缺陷,也避免了只用参数/r时如果使用通配符不能匹配目录的问题,简直就是优差互补、天生一对!

4.10 参数/f——为解析文本而生

for命令的参数如果为/f,功能就变成了解析文本,需要和其他能够生成文本的命令使用、或者指向一个文本文件、或者直接输入一个字符串文本。基本语法规则如下。

以下是在cmd窗口直接执行的语法示例,在bat脚本中的语法示例据此简修、即可告成。

FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
FOR /F ["options"] %variable IN ("string") DO command [command-parameters]
FOR /F ["options"] %variable IN ('command') DO command [command-parameters]

我们可以看到,总的来说有3种用法,第一种是文本文件集合(不用边界符包裹)、第二种是字符串文本集合(需要双引号包裹)、第三种是能输出文本的命令(需要单引号包裹)。

其实这3种,只是输入文本的方式不同,最终文本的存在形式是可以一样的,因此不作为重点去阐释。

我们重点研究字段["options"],字段[command-parameters]和字段(set)的边界符作为次要点顺便提到。

下面把相关帮助信息截取出来,让我们聚焦于此;有个小错误是需要去发现的。

eol=c           - 指一个行注释字符的结尾(就一个)
skip=n          - 指在文件开始时忽略的行数。
delims=xxx      - 指分隔符集。这个替换了空格和制表符的默认分隔符集。
tokens=x,y,m-n  - 指每行的哪一个符号被传递到每个迭代的 for 本身。这会导致额外变量名称的分配。m-n格式为一个范围。通过 nth 符号指定 mth。如果符号字符串中的最后一个字符星号,那么额外的变量将在最后一个符号解析之后分配并接受行的保留文本。
usebackq        - 指定新语法已在下类情况中使用:在作为命令执行一个后引号的字符串并且一个单引号字符为文字字符串命令并允许在 file-set中使用双引号扩起文件名称。

eol参数和skip参数都是用来指定需要省略的行。

(1)eol参数的帮助信息认为“行注释字符处于句尾”,这是错的,应该是句首。

eol参数的值c是一个符号,默认的行注释字符是分号【;】,因此如果不想要激活行注释,参数写成【eol=】(不给eol赋值),如果想要更换行注释字符(比如感叹号),参数写成【eol=!】。

“就一个”的意思是,每一个for命令只能指定一个行注释字符,要么是【;】、要么是【!】。

(2)skip参数的值n决定了会有多少在开头位置的行会被省略,假设后面的set只有3行文本,而skip=2,并且最后一行文本没被“行注释”,那么结果只输出一行文本。

(3)delims参数用于指定每一行的文本该如何切割与划分,如果未指定就默认为空格或跳格符(制表符)。如果不想做任何切割,需要给参数赋空值,也即【delims=】。如果要更换切割符,可以赋其他值,例如逗号【,】,则添加【delims=,】。

(4)tokens参数用于选取每一行的被切割的片段来传给variable。如果不指定,默认只选中第一个片段传递给variable。帮助信息的所谓“符号”,其实翻译不够准确,译为“片段”则恰到好处。

for命令是以行为单位来执行命令的,而非片段。

允许选定多个片段,索引号之间用逗号分隔,例如tokens=1,3,4。选定多个片段不代表%variable需要写成多个,只需要写一个首变量即可,比如%i;但是后面的parameters则需要根据tokens选中的片段数来设定同样数量。首参数命名与variable相同,只是后面的参数命名必须依26字母顺序进行。如果首参数是%a,那么后面的参数依次就必须为%b、%c、%d、%e......

如果需要连续选定多个片段(不代表将其输入给一个variable),在首索引号和尾索引号之间用破折号连接,例如tokens=2-4,可以将片段2、3、4传给三个parameter。

如果需要将切割的各个片段融合成一个片段,将这个片段传给一个parameter,可以用梅花符垫底,例如如果一共有5个片段,添加参数【tokens=1-3,*】,可以将片段4和片段5融合在一起(不会被delims指定分割)传给最末尾的parameter。

遗憾的是,只能从右向左融合没指定的片段,直到下一个是被指定的片段就停止融合。也即假设片段一共7个,参数是【tokens=1-2,4-5,*】,最后片段3会被丢弃,只能融合片段6和片段7。

tokens中的索引号从左到右不一定要升序摆放,也可降序,但是对最终parameter接收到的片段不影响。例如【tokens=1-2,4-5,*】其实和【tokens=4-5,1-2,*】一模一样,【tokens=2-3,1,*】其实和【tokens=1,2-3,*】一模一样。

(5)当set是文本文件集合并且文件名中含有空格时,为了避免路径空格问题和特殊符号问题(比如包括&时),用传统的加双引号的方式去解决,会与for命令 /f参数的语法规则冲突(因为加双引号属于字符串文本集合,就会把文件名当成普通的字符串处理,岂能如此哦)。

于是,CMD开发者设定了一个开关参数usebackq,如果添加这个参数就打开了后备规则开关。

什么叫后备规则呢?意思就是另一套规则。

原先的规则如下表

文本文件集合 字符串集合 文本生成命令
无符号 双引号"" 单引号''

后备规则如下表

文本文件集合 字符串集合 文本生成命令
双引号"" 单引号'' 后引号``(和波浪符同键)

激活后备规则后,再用双引号对存在空格或特殊符号的文件路径进行包裹,就不会被“错认”成普通字符串啦!

下面给出案例1,set是文本文件。

在D盘的路径【D:\Program Files\python】下新建一个txt文件(其中的逗号是中文,所以下面的代码中delims也要赋予中文逗号),内容如下。

创建一个bat脚本文件,代码如下

@echo off
chcp 65001
for /f "usebackq eol=! skip=2 delims=, tokens=1-2" %%i in ("D:\Program Files\python\tool.txt") do echo %%i+%%j
pause>nul

双击执行,结果如下图

下面讲解为什么是这个结果!

首先,因为【skip=2】,所以头两行直接被忽略了。

其次,因为【eol=!】,所以有几行首符号为分号,但并没有被注释(忽略)掉。

再者,因为【delims=,】,这个逗号是中文逗号,所以涉及到以中文逗号隔开诗词上下句的,分隔符都被替换成加号。

另外,由于文件名路径涉及到空格,所以需要加上备用规则开关【usebackq】。

最后,由于【tokens=1-2】,只保留了前两个片段,所以最后一行的苏轼情词“十年生死...”的最后一句“自相忘”被忽略掉了。

下面给出案例2,set是文本生成命令。

假设F盘下的软件目录中有一堆文件夹需要添加后缀。

我们可以随便在一个目录新建一个bat脚本,代码如下。

@echo off
chcp 65001
for /f "delims=" %%i in ('dir /b /a:d F:\软件') do ren "F:\软件\%%i" "%%i_欢迎报考重庆大学"
pause>nul

执行结果如下。

下面讲解为什么是这个结果!

首先,我们要搞懂命令【dir /b /a:d F:\软件】的输出文本是什么。如下图所示,每一行都是一个文件夹名称。

其次,因为有两个文件夹的名称中有空格,所以命令【ren】中的原始和目标名称都需要加双引号。

最后,由于默认的分割符是空格,所以还需要添加参数【delims=】来取消对存在空格的文件夹名称的分割。

下面给出案例3,set是普通字符串文本。

假设我是一个强迫症,想把李荣浩发的微博中的空格转成中文逗号。

我创建了一个bat脚本,代码如下。

chcp 65001
@echo off
for /f "tokens=1-3" %%i in ("消失了一段时间 两万字 写完收工") do echo %%i,%%j,%%k
pause>nul

执行结果如下。

希望李荣浩和他的粉丝会满意。

五、条件判断语句——if xx else if xx else xx

5.1 if语句的经典结构与排版技巧

参考文章:bat脚本里面if else if的写法 - 熊仔其人 - 博客园

5.1.1 一行铺满式

对写代码的人来说非常简单,但是对于看代码的人来说可读性较差。

if condition1 (command1) else if condition2 (command2) else (command3)

if部分必须存在;if和else if可以搭配存在而不必存在else部分,同理if和else可以搭配存在而不必存在else if部分;但是else部分只能出现一次;else if部分理论上可以无限叠加。

如果你把条件划分得很细,那么可以添加很多个else if部分。

除非只有if 部分(也即没有else if和else部分),否则必须在各个执行语句部分(commandx)用圆括号圈起来,不然就会执行错误。

@echo off
chcp 65001
if "abc"=="ABC" echo "两字符串相等" else echo "两字符串不相同"
pause

不信你们可以试试把上面这块代码复制到一个Bat文件中双击执行, 最后肯定打印不出任何结果,仿佛if语句不存在一样,就直接执行Pause了。

如果写成5.1.1这样一行的形式,才可以不加圆括号;而如果写成5.1.2和5.1.3那样多行形式,就必须加圆括号。

5.1.2 多行“括号”排版式

对“一行铺满式”进行重新排版,代码主体和执行效果不变。

主要利用圆括号【()】进行优化排版。

可读性好,兼容性略差。

if condition1 (command1
) else if condition2 (command2
) else (command3
)

5.1.3 多行“括号”、“换行”增强式

在5.1.2的基础上进一步优化排版,增加了换行接续符【^】,让代码的兼容性大幅提高。

if condition1 ( command1
) ^
else if condition2 ( command2
) ^
else if condition3 ( command3
) ^
else ( command4
)

5.2 if语句可判断的条件有6种

if语句的经典关键字【not】代表真值逆转,而关键字【/i】代表不区分大小写,这两个关键字其实都可以在下面的5.2.1-5.2.4小节中使用。

但是5.2.1小节和5.2.4小节,本来就是不区分大小写的。

也就是说如果存在文件【paths.txt】,你用【PATH.txt】去判断最后也会返回真值;如果已经有变量var1被定义,那么在判断VAR1时也会认为已被定义。

5.2.1 判断某个文件(夹)是否存在

语法规则如下。

if [not] exist <filename> <command> [else <expression>]

例如,我想要判断桌面路径的某文件是否存在,将下面的代码复制到Bat文件双击执行。

@echo off
chcp 65001
if exist D:\D-desktop\test5\paths.txt echo "路径所指文件存在"
pause

结果如下,当然存在啦!

有些读者可能会疑惑,能否判断某一个文件夹是否存在吗?答案是能。

我们稍加修改一下代码

@echo off
chcp 65001
if exist D:\D-desktop\test5 echo "路径所指文件夹存在"
pause

然后双击执行,结果如下;完美实现,欧耶!

注意:不区分大小写,哪怕文件名或路径字符串中的字母写成与实际大小写不一致,最后也会返回条件真值。

5.2.2 判断两个字符串之间是否相同

语法规则如下。字符串可以不用加引号,加了可以避免字符串空格问题。

if [not] <string1>==<string2> <command> [else <expression>]

假如我想要比较两个字符串变量是否相同,可以使用以下代码。

@echo off
chcp 65001
set var1=abc
set var2=ABC
if %var1%==%var2% echo "两字符串相同"
pause

在bat文件中执行上述代码,结果如下。

没有输出“两字符串相同”,说明这两个变量其实不相同,因为区分大小写。

另外,为了避免字符串空格问题,可以在字符串两边加引号、中括号、花括号,这样实际上也是将包裹在外边的符号看成和字符串一体的部分进行比较。

5.2.3 能比较两个数之间的大小

此用法属于扩展用法,需要cmd打开命令扩展功能;由于此功能是默认打开的,因此也就很容易被忽视使用前提。

语法规则如下。字符串可以不用加引号。

if [/i] <string1> <compareop> <string2> <command> [else <expression>]

如果两个字符串全都是数字,那么比较的就是两个数字量的大小;而如果两个字符串有至少一个不是纯数字,就会逐序比较ASCII码。

if语句支持的关系运算符如下表。

  • EQU - Equal to
  • NEQ - Not equal to
  • LSS - Less than
  • LEQ - Less than or equal to
  • GTR - Greater than
  • GEQ - Greater than or equal to

在bat文件中执行以下代码,比较两个纯数字字符串的大小。

@echo off
chcp 65001
set var1=458
set var2=456
if %var1% GTR %var2% echo "变量var1大于变量var2"
pause

结果如下。

在bat文件中执行以下代码,比较非纯数字字符串的大小。

@echo off
chcp 65001
set var1=rth
set var2=ytb
if %var1% GTR %var2% (echo "变量var1大于变量var2") else (echo "变量var1小于变量var2")
pause

结果如下。

5.2.4 能判断某个变量是否被定义

此用法属于扩展用法,需要cmd打开命令扩展功能;由于此功能是默认打开的,因此也就很容易被忽视使用前提。

语法规则如下。

if defined <variable> <command> [else <expression>]

下面给一个例子,在BAT文件中执行下面的代码。

@echo off
chcp 65001
set var1=rth
set var2=ytb
if defined var1 (echo "变量var1存在") else (echo "变量var1不存在")
if defined VAr2 (echo "变量var2存在") else (echo "变量var2不存在")
pause

结果如下,我们可以看到不是写成【%var1%】的带双百分号的形式,另外虽然写成【VAr2】也因为不区分大小写而取得了条件真值。

5.2.5 能判断错误码是否大于等于某个具体值

语法规则如下。如果errorlevel的值大于或等于后面的number(数字取值范围0~255),就返回条件真值,而直接执行紧跟其后的command命令。

if [not] ERRORLEVEL <number> <command> [else <expression>]

例如我在bat文件中加入搜索tool.txt的代码,然后打印搜索结果退出码。

@echo off
chcp 65001
dir tool.txt
echo %errorlevel%
if errorlevel 1 (echo "大于等于1") else (echo "小于1")
pause

执行结果如下。

5.2.6 能判断cmd版本号是否大于等于某个具体值

此用法属于扩展用法,需要cmd打开命令扩展功能;由于此功能是默认打开的,因此也就很容易被忽视使用前提。

语法规则如下。如果cmdextversion的值大于或等于后面的number,就返回条件真值,而直接执行紧跟其后的command命令。

if cmdextversion <number> <command> [else <expression>]

下面给个例子,在bat文件中执行以下代码。

@echo off
chcp 65001
echo %cmdextversion%
if cmdextversion 1 (echo "大于等于1") else (echo "小于1%")
pause

结果如下,因为cmd版本是2,所以最后输出了“大于等于1”。

六、循环命令——call与%0

在bat脚本中直接执行【%0】或通过call命令调用%0,都会导致Bat文件陷入没有尽头的死循环。

@echo off
chcp 65001
echo "大哥出生了"&echo "二弟出生啦"&echo "三妹出生啦"
(
echo "大哥出生了"
echo "二弟出生啦"
echo "三妹出生啦"
)
call %0
pause

你们可以把上面这块代码复制到bat文件中,双击执行试试,绝对会不断地“生孩子”哈哈哈。

七、循环命令——goto和if

下面给出一个例程。if语句用来判断是否满足继续循环条件,满足就执行goto进入循环,不满足就不执行goto语句,而继续向下执行。

@echo off
set var=0
rem ************循环开始了
:continue
set /a var+=1
echo 第%var%次循环
if %var% lss 100 goto continue
rem ************循环结束了
echo 循环执行完毕
pause

一个bat文件中,可以不止包括一个goto循环;方法跟上面例程一样 ,往后继续扩展,如下所示就是属于加了两个循环在一个bat文件。

@echo off
set var=0
rem ************循环开始了
:continue1
set /a var+=1
echo 第%var%次循环
if %var% lss 100 goto continue1
:continue2
set /a var+=1
echo 第%var%次循环
if %var% lss 100 goto continue2
rem ************循环结束了
echo 循环执行完毕
pause

【批处理DOS-CMD命令-汇总和小结】-跳转、循环、条件命令(goto、errorlevel、if、for[读取、切分、提取字符串]、)cmd命令错误汇总,cmd错误相关推荐

  1. python跳过本次循环_python跳过循环

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 我如何跳过循环使用pdb.set_trace()? 例如, pdb.set_tr ...

  2. 【批处理DOS-CMD命令-汇总和小结】-CMD窗口的设置与操作命令(cd、title、mode、color、pause、chcp、exit、goto :eof)

    一. 改变cmd窗口当前目录--资源管理器或cd 具体方法详见我的专栏中另一篇文章的章节1.5. [批处理DOS-CMD命令-汇总和小结]-文件与目录操作命令(md.rd.xcopy.dir.cd.s ...

  3. 【批处理DOS-CMD命令-汇总和小结】-Cmd窗口中常用操作符(<、<<、<、>、>>、>、、、||、|、()、;、@、““、‘‘)

    一.重定向操作符--<.<<.&<.>.>>.&> 1.1 清除重定向--<和> cmd>file     //cmd ...

  4. 【批处理DOS-CMD命令-汇总和小结】-cmd扩展命令、扩展功能(cmd /e:on、cmd /e:off)

    一.问题背景 在打印erase的帮助信息时,提示如果启用扩展命令,...,如果是这个命令出现还好,没有引起我的注意,但是我发觉好多命令的最后都有提到这个扩展功能了. 不得已去百度谷歌查一通,资料很少, ...

  5. 【批处理DOS-CMD命令-汇总和小结】-cmd的内部命令和外部命令怎么区分,CMD命令和运行(win+r)命令的区别,cmd的命令扩展(扩展命令)

    一.cmd的内部命令和外部命令怎么区分? 区分规则很简单,在cmd窗口中用help命令能打印出来的命令都是cmd内部命令,没包括在里面的都是外部命令. D:\>help 有关某个命令的详细信息, ...

  6. 【批处理DOS-CMD命令-汇总和小结】-将文件夹映射成虚拟磁盘——subst

    一.subst命令的意义 有时候文件(夹)的目录很深,每一次打开的时候需要一直切换目录,有了镜像虚拟磁盘subst命令,临时地将文件夹映射成磁盘显示在我的电脑中,就会让工作变得更轻松,可以节约很多时间 ...

  7. 【批处理DOS-CMD命令-汇总和小结】-磁盘管理和修复、磁盘和分区属性的管理、设置隐藏分区卷(convert、diskpart、chkdsk)

    一.磁盘格式转换命令--convert 打印convert命令的帮助信息 如果想要将c盘从fat转到ntfs,可以执行命令[convert c:/fs:ntfs]. 二.磁盘或分区管理--diskpa ...

  8. 【批处理DOS-CMD命令-汇总和小结】-上网和网络通信相关命令(netsh)

    一.netsh命令的简介 netsh(Network Shell)是一个windows系统本身提供的功能强大的网络配置命令行工具,可用来修改windows的ip.网关.dns等信息. netsh是一个 ...

  9. 【批处理DOS-CMD命令-汇总和小结】-显示文本文件内容(type),对文本文件的整行字符串进行排序(sort)

    一.读取文本文件--type 先看一下type的帮助信息. 由此可见,type的作用就是来读取文件的内容的,内容可以参考"以记事本方式"打开的情况,如果是txt.md.bat这种纯 ...

最新文章

  1. 全球3D机器视觉技术引领者,银牛微电子强势登陆中国市场
  2. MySQL抓包工具:MySQL Sniffer【转】
  3. perl数组硬引用_perl引用和数组 - SibylY的个人空间 - OSCHINA - 中文开源技术交流社区...
  4. is属性用法 vue_Vue中is属性的用法 可以动态切换组件
  5. 面向对象程序设计的术语
  6. 团队作业3——需求改进系统设计
  7. bartender实现即扫即打印功能扫描完后自动打印_日本彩色激光打印机推荐人气排名15款...
  8. 无锁队列详细分解 — 顶层设计
  9. Java的GUI学习三(frame)
  10. SqlServer2008 不允许保存更改
  11. JDK 的下载与安装(非常详细!)
  12. 完整最新的f1比赛规则
  13. 获取并解析心知天气数据
  14. ADIS16465 加速度和陀螺仪数据融合及调试(kalman滤波)
  15. apache 支持下载rar格式文件问题
  16. Git(码云)配置精干版
  17. 老婆背着我在豆瓣发帖:没事别嫁程序员!
  18. Failed to connect to the host via ssh: Control socket connect(/root/.ansible/cp/xxx): Connect
  19. 2012.4.29~2012.5.1平谷(二)
  20. 神舟计算机调研报告,6月笔记本品牌关注调研报告 神舟居榜首

热门文章

  1. python中用BBP公式计算π
  2. python之flask_sqlalchemy的使用及详解
  3. FFmpeg从入门到精通命令
  4. 什么是性能测试?性能测试目的?性能测试的主要分类以及性能测试的常用指标?
  5. 计算机网络技术专业就业前景分析。
  6. 小岳岳吐槽房子隔音差:买房小心隔墙有耳
  7. uCOS2的events事件源码分析-第4季第5部分-朱有鹏-专题视频课程
  8. 【历史上的今天】4 月 14 日:Ruby 之父诞生;GDPR 首次颁布;Lindows 更名为 Linspire
  9. Python基础:进程、线程、协程(2)
  10. guava学习:RangeSet