收藏 分销(赏)

批处理FOR命令简析.doc

上传人:xrp****65 文档编号:7679218 上传时间:2025-01-12 格式:DOC 页数:14 大小:77KB
下载 相关 举报
批处理FOR命令简析.doc_第1页
第1页 / 共14页
批处理FOR命令简析.doc_第2页
第2页 / 共14页
点击查看更多>>
资源描述
整理延迟变量扩展介绍补充 引用变量的值在批处理中习惯叫变量扩展。 使用一对百分号括起变量名(如:%VAR%)的形式表示变量扩展。 延迟引用变量的值叫延迟变量扩展。 使用一对感叹号括起变量名(如:!VAR!)的形式表示延迟变量扩展。 是指变量扩展这个动作被推迟。 有了变量扩展。为什么又多出一个延迟变量扩展呢? 先看下面程序片段1: set VAR=before if "%VAR%" == "before" ( set VAR=after echo %VAR% ) 运行结果是什么? 结果是:before 而正确逻辑结果应是:after 为什么会发生这样的事呢? 原来命令解释程序(cmd)在执行复合语句之前做了预处理,即读入复合语 句时做了变量扩展。对于程序片段1,命令解释程序读入if复合语句,接着 做VAR变量扩展,由于此时set VAR=after语句还没有执行,VAR变量的值 未被改变仍然是before,因此,%VAR%扩展为before ,echo %VAR%语句 变为echo before 。然后,命令解释程序从if语句的开头开始执行,很明显 改变VAR变量的值对echo before语句没有影响(因%VAR%已提前扩展为 before),执行结果是before 。 扩展后的程序片段1: set VAR=before if " before " == "before" ( set VAR=after echo before ) 再看一个程序片段2:(给当前目录文件列表的每行加上序号) set count=0 for %%i in (*) do ( set /a count += 1 echo %count% %%i ) 运行结果:所有序号都是0 不是正确的逻辑结果。 原因与程序片段1一样。命令解释程序读入for复合语句,接着做count变 量扩展,由于此时set /a count += 1语句还没有执行,count变量的值未被改 变仍然是0,因此,%count%扩展为0 ,echo %count% %%i语句变为echo 0 %%i 。然后,命令解释程序从for语句的开头开始执行,很明显改变count 变量的值对echo 0 %%i语句没有影响(因%count%已提前扩展为0),执行 结果的序号是0 。 扩展后的程序片段2: set count=0 for %%i in (*) do ( set /a count += 1 echo 0 %%i ) 为了解决以上问题,命令解释程序增加了在运行时才进行变量扩展的功能 --"延迟变量扩展"。即引入延迟变量扩展的表示形式(使用一对感叹号括起 变量名,如:!VAR!),这里简称它为"延迟变量扩展表示式"。命令解释程序 在读入语句时,遇到"延迟变量扩展表示式",对表示式中的变量不做变量扩 展,而推迟到在执行时才做变量扩展。因此,在if、else、for和用"& | && ||"等连接起来的复合语句中,如果在变量扩展前变量的值发生了变化,就 要将变量扩展改为延迟变量扩展,令变量扩展在执行时才进行,从而得到变 化了的变量值。 因此,要将以上程序片段中变量扩展改为延迟变量扩展,修改后的程序片段: set VAR=before if "!VAR!" == "before" ( set VAR=after echo !VAR! ) set count=0 for %%i in (*) do ( set /a count += 1 echo !count! %%i ) 但是,默认情况下,"延迟变量扩展"功能是停用的。 启用这项功能,有几种方法: 1. 运行cmd /v: on 进入命令提示符窗口,然后输入命令。 2. 直接运行cmd /v: on /c "commands" 3. 在批处理文件里使用这项功能之前设置:setlocal enabledelayedexpansion 4. 设置注册表,让cmd默认情况下启用这项功能,可参考命令:cmd/? 总结以上要点,将程序片段2完善后,得到以下批处理程序—myDir.bat: :: 给当前目录文件列表的每行加上序号 @echo off setlocal enabledelayedexpansion set count=0 for %%i in (*) do ( set /a count += 1 echo !count! %%i ) 运行程序,结果正确。 如果不想为何时使用延迟变量扩展而烦恼,可以一开始就使用延迟变量扩展, 替代变量扩展。但是,迟变量扩展的自身嵌套或者和变量扩展相互嵌套使用, 也存在问题。 另外,对于"延迟变量扩展"问题,有另一巧妙的解决方法(叫call方法),将 call命令放在需要"延迟变量扩展"的语句前面,同时变量扩展要再用一对百分 号括起。这时call的作用相当于"延迟变量扩展"功能。其实call命令具有对 它后面命令做预处理和执行功能。 再看程序片段1: set VAR=before if "%VAR%" == "before" ( set VAR=after echo %VAR% ) 改进后: set VAR=before if "%VAR%" == "before" ( set VAR=after call echo %%VAR%% ) 运行结果是:after 为什么运行结果是:after 呢? 命令解释程序读入if复合语句,接着做预处理,即对%VAR%做变量扩展。 而对于%%VAR%%,是这样处理,当遇到连续二个百分号时,会去掉一个, 剩下一个只是字面意思而没有变量扩展功能意思,于是VAR也当作字面意 思。接着遇到后面连续二个百分号时,同样会去掉一个。预处理后,call命 令变为call echo %VAR%,然后,命令解释程序从if语句的开头开始执行, 执行到call命令时,call命令对echo %VAR%再做一次预处理,即对%VAR% 做变量扩展,由于之前VAR变量被赋予after,于是%VAR%扩展为after,接 着,执行echo after。所以,运行结果是:after。 总结:要注意复合语句中的变量扩展。如果在变量扩展前变量的值发生 了变化,就要将变量扩展改为具有"延迟变量扩展"功能的形式。 题外话:"延迟变量扩展"问题,是个害人的陷井,如马路上的沙井被拿去井 盖一样,行人一不小心就陷进去。让新手头大,让老手麻烦。这是cmd的设 计缺陷,将修补缺陷的工作留给用户(自行盖井盖)是极不负责任! use willsort老大上面的帖子,对于新手来说比较难理解。不过没关系,我们先分析一个例子,同样是引用willsort老大的。本例启用了变量延迟,是个正确的例子! 例1: @echo off & setlocal EnableDelayedExpansion for /f "tokens=* delims=" %%i in ("Hello world.") do ( set n=%%i set n=!n:ld.=t! set n=!n:o w= S! set n=!n:He=Wi! echo !n! ) pause 将上面代码保存为.bat双击执行后会显示“Will Sort”字符串,下面将讲解每个语句的意思: 1.@echo off & setlocal EnableDelayedExpansion 关闭命令回显,并启用变量延迟 2.for /f "tokens=* delims=" %%i in ("Hello world.") do ( for命令及其参数的使用,请大家在论坛里搜索相关字眼。限于篇幅问题,这里不作讨论。如果此时你不明白它的意思,那么你就当它的作用是把字符串“Hello world.”赋值给%%i好了,当然这只是权宜之计,以后一定要学习for的使用! 3.set n=%%i 把%%i的值(即Hello world.)赋予给变量n,这个大家都知道吧 4.set n=!n:ld.=t! 这里要讲讲set替换字符的功能了。这个语句的意思是,先获取变量n的值(此时n的值是“Hello world.”),然后将字符“t”替换字符“ld.”,然后再将替换后的结果再次赋值给变量n(此时n的值变为“Hello wort”)。至于set替换字符的编写格式,大家可以在CMD键入“set/?”找到“%PATH:str1=str2%”这段有说明 5.set n=!n:o w= S! 意思和上句一样,只是替换和被替换的内容不同。它是将“ S”替换“o w”(注意S前面和w前面都有个空格),其实willsort老大是想证明set替换字符是支持句点和空格的(第4句“ld”后面有个.)。此时n的值为“Hell Sort” 6.set n=!n:He=Wi! 这句不用说了吧,执行完这句后n的值为“Will Sort” 7.echo !n! 显示变量n的值 需要注意的是,一旦启用了变量延迟,就要用!号把变量括起来,而不能用%号。 好了,每句的意思已经说完了,下面要讲本帖真正要讨论的变量延迟的问题。 这里又要引用Will Sort老大的说明:当CMD读取for语句时,其后用一对圆括号闭合的所有语句将一同读取,并完成必要的预处理工作,这其中就包括环境变量的扩展,所以在for中的所有语句执行之前,所有的环境变量都已经被替换为for之前所设定的值,从而成为一个字符串常量,而不再是变量。 而为了能够在for语句内部感知环境变量的动态变化,CMD设计了延迟的环境变量扩展特性,也就是说,当CMD读取了一条完整的语句之后,它不会立即执行变量的扩展行为,而会在某个单条语句执行之前再进行扩展,也就是说,这个扩展行为被“延迟”了。 总的来说是,在没有启用变量延迟的情况下,凡是在括号内(即do里面)的变量,在执行for语句之前,就已经被替换成for语句之前其它命令对该变量所赋予的值。这句话不懂没关系,下面再看一个例子,看完你就会明白。 例2: @echo off for /f "tokens=* delims=" %%i in ("Hello world.") do ( set n=%%i set n=%n:ld.=t% set n=%n:o w= S% set n=%n:He=Wi% echo %n% ) pause 这和前面的例子差不多,只是所有!号都换成%号,这是个错误的例子。因为它没有启用变量延迟,也没有使用!号把变量括起来。我们看到它的执行结果是显示“ECHO 处于关闭状态”。 为什么会这样呢?原因是,在没有启用变量延迟的情况下,凡是在括号内(即do里面)的变量,在执行for语句之前,就已经被替换成for语句之前其它命令对该变量所赋予的值。 则是说在本例中的以下几句 set n=%%i set n=%n:ld.=t% set n=%n:o w= S% set n=%n:He=Wi% echo %n% 第一句能正常执行并达到它的目的,因为它只是单纯地将%%i的值赋予给变量n,所以没有任何问题。其它几句属这样情况:早在for语句执行前,CMD就急不切待地将这几句里面的所有变量n一同执行替换行为,替换为for之前,其它命令对n所设置的值,从而使n变成一个常量。但在本例中,for语句之前只有@echo off这句,并没有其它命令对n作过任何赋值行为,所以在for之前,变量n的值为空值。即是说,set n=%n:ld.=t% 这句里面的变量n,在CMD读取(注意是读取不是执行)完整个for语句后(这时还未轮到set执行自己的任务),就立刻被替换为一个空值,一个空值里面没有任何东西,所以就不存在一字符替换另一字符这种说法(没有东西怎么替换?)。最终到执行set n=%n:ld.=t%语句时,它只是获取一个空值,再给变量n赋予空值而已。其它几句也是一样原理。 所以,最后echo %n%的时候变量n还是个空值,而echo命令没有东西可以显示,就只有显示“ECHO 处于关闭状态”这句来说明自己的状态 通过这个例子的说明,相信大家已经知道变量延迟的作用吧!我们再回头来看看例1。 启用变量延迟后,在执行 set n=!n:ld.=t! set n=!n:o w= S! set n=!n:He=Wi! echo !n! 这些语句前,它们里面的变量n不会马上被CMD替换(启用延迟后,CMD变得有耐性啦^_^),而未被替换的话,那么n就还是变量,而不是常量。等到执行set n=!n:ld.=t!等这几句时,变量n才被替换。这样每个set命令都能感知变量n的任何变化,从而作出正确的替换行为。这就是变量延迟啦! 可跳过: 什么,说我讲得不好?没办法啊,因为偶太菜啊,只知道这些。偶只是淘两顿饭吃而已,望大家谅解啊,不要再拿砖头砸偶。。。不然偶就~~~~~~~~~~叫救命!^_^ 这是正文不可跳过: 不要以为只有for才要用变量延迟,下面这个例子同样需要 例3:这是个错误的例子 @echo off set mm=girl&echo %mm% pause 执行后依然显示“ECHO 处于关闭状态”。 原因是没有启用延迟,而且在set mm=girl&echo %mm%语句前没有其它命令对mm进行赋值。这时当CMD执行set mm=girl&echo %mm%语句前,就已经急不切待地把变量mm的值替换了,而又因为前面没给mm赋值,所以mm被替换为空值,变成常量。等到echo命令执行时,它其实是echo一个不会变化的常量,本例中即是空值。 有人会问,echo前面不是给mm赋值了吗? 这个就要关系到CMD解释命令的步骤,大家可以参详本帖开头willsort的帖子。 总的来说是,如果不启用变量延迟,在本例中,echo是不会理会也不会知道,它前面(指同一行语句)是否有其它命令给mm赋值。它只会从set mm=girl&echo %mm%这句以上的语句中获取它所要显示的变量的内容,也就是说,上一行或上几行的命令将mm设置成什么值,echo命令就显示什么值。 大家这样做就明白了: @echo off set mm=boy set mm=girl&echo %mm% pause 看看显示什么结果就知道了! 这样编写例3才正确: @echo off&setlocal EnableDelayedExpansion set mm=girl&echo !mm! pause 开启了变量延迟,变量扩展(替换)的行为就推迟到echo命令执行时,这时echo能感知它前面的命令(本例的set)对变量mm做了什么“坏事”,从而作出正确的判断并执行 好了全篇完了,下课! =========================================================================================== =========================================================================================== 转自“简析环境变量和变量延迟特殊字符以及中介法的微妙关系” 简析环境变量和变量延迟特殊字符以及中介法的微妙关系 已作修改 本文主要以例子展开了讨论: 对每个例子的结果进行分析;并揭示其中的一些现象; 例一: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off set "var=kljlk!tsd!21%mk%gd" set var 结果为 Quote: var=kljlk!tsd!21gd (注意到 此时setlocal默认为disabledelayedexpansion !!不被识别) 因此把%mk%看做环境变量被替换掉;而mk没有被定义因此%mk%被替换为空 再来看加了setlocal enabledelayedexpansion后会是怎样; 例二: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off&setlocal enabledelayedexpansion set "var=kljlk!tsd!21%mk%gd" echo.!var! 结果为 Quote: kljlk21gd 注意到 此时setlocal声明为enabledelayedexpansion 因此把!tsd!和%mk%均被看做环境变量而被替换掉;而tsd和mk没有被定义因此均被替换为空 (由于优先级的不同;%mk%将在预处理被先被替换,然后!tsd!被替换;原因后面有讲到) 如果把kljlk!tsd!21%mk%gd放到t.txt然后 再通过%%a做为中介进行传递后会有什么现象呢 例三: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off for /f "delims=" %%a in (t.txt) do ( set "var=%%a" set var ) 结果为: Quote: var=kljlk!tsd!21%mk%gd 如果按照上面所说的此时%mk%应该被替换为空; 但是这里经过一个%%a的中介;此时的结果不会把%mk%替换;即使mk环境变量有定义也不会替换掉;而是按字面输出; 实际上这跟cmd的预处理机制有关; 当CMD读取for语句时,他的所有语句将一同被读取,并完成预处理工作,这其中就包括环境变量%%的扩展; 因为字符串在文本中 并没有以字符串的形式出现在 for语句中;因此其中的%%躲过了预处理机制; 但是当启用延迟环境变量后 例如: 例四 CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off for /f "delims=" %%a in (t.txt) do ( set "var=%%a" set var ) 结果为: Quote: var=kljlk21%mk%gd 首先他会和上面一样对for所有语句一起读取;完成对%%的扩展;(%%的优先级高) 然后当CMD读取了一条完整的语句之后, 它不会立即执行变量的扩展行为,而会在某个单条语句执行之前再进行扩展,也就是说,这个扩展行为被“延迟”了 例如在set "var=%%a"时 只有执行到这条命令前才会对这条命令进行扩展; 而此时%%a已经被kljlk!tsd!21%mk%gd所代替;因此将会对其中的!!进行扩展. 他会识别其中是否存在!;此时的扩展只处理!!而不处理%%(因为对%%的处理过程在先前已经进行完;而那时因为字符串在文本中 并没有以字符串的形式出现在 for语句中;因此其中的%%躲过了预处理机制;); 然而只有当该字符串中含有!时, cmd才会对该字符串进行再次处理; 举例: 例五: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off&setlocal enabledelayedexpansion for /f "delims=" %%a in (t.txt) do ( set "var=%%a" set var ) t.txt的内容 Quote: kl&>jl^k!tsd!21%mk%gd kl&>jlk^!tsd^!21%mk%gd dgssdgdg^gds^gdsa 结果为: Quote: var=kl&>jlk21%mk%gd var=kl&>jlk!tsd!21%mk%gd var=dgssdgdg^gds^gdsa 注意到 第一行的 ^ 已经不存在了;(所在行包含!!) 而第三行的^没有被处理;(所在行不包含!!) (包含一个!也会对^进行处理;) 由此可见 启用延迟环境变量后 ; 在每次执行语句前; cmd会检查是否含有!; 如果存在就对其进行必要的预处理后再执行. 注:此时setlocal默认为disabledelayedexpansion因此!tsd!是因为不给识别而不被替换和%mk%不被替换的原因不同. 如果想强迫进行二次转变(即把%mk%的值按其环境变量值警醒替换)再赋值给var可以使用 call set "var=%%a" 也就是说: 例六: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off set "mk=152" for /f "delims=" %%a in (t.txt) do ( call set "var=%%a" set var ) 的结果则为: Quote: var=kljlk!tsd!21152gd call set "var=%%a" 实际上是让命令解释器再来一次预处理来达到目的的 再来看%%a另一个例子 例七: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off echo ^&*&^^&%$%^%$#$1213<>:Lksgsd 结果为: Quote: &* '^' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 'Lksgsd' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 大家都知道这是因为里面有特殊字符; 但是看下面的例子;把^&*&^^&%$%^%$#$1213<>:Lksgsd放到t.txt中;用%%a做为中介后: 例七: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off for /f "delims=" %%a in (t.txt) do ( echo.%%a ) 结果则为: Quote: ^&*&^^&%$%^%$#$1213<>:Lksgsd 所有的都是原样输出;丝毫没有因为特殊字符而受影响;(原因上面已讲到) 注:如果t.txt中有直接回车的空行则会跳过;原因在for本身而不是%%a; 再来个!var!的特殊例子 例八: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off set "var=^&*&^^&$^$#$1213<>Lksgsd" echo.%var% 结果为: Quote: 此时不应有 >。 结果出错 做如下的修改 例九: CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off&setlocal enabledelayedexpansion set "var=^&*&^^&$^$#$1213<>Lksgsd" echo.!var! 结果为: Quote: ^&*&^^&$^$#$1213<>Lksgsd 结果正确; 但是此种方法仍然受上面所述的原因影响; 比如如果var=后面的值含有!!和%%等则会发生替换;还有比如中间有:则会与set发生关系而导致结果不准确; 当然可以利用%%a中介可以避免 %% 被替换;但是!!却有上述原因没法逃过被替换; 最后给出个综合应用的例子: 对文件指定行进行输出;不论是否含有特殊字符: 这里就把整个文件输出;对指定行可以修改为 findstr /n .* test.txt^|findstr "^10:" 等; CODE: [Copy to clipboard] -------------------------------------------------------------------------------- @echo off for /f "delims=" %%a in ('findstr /n .* test.txt') do ( set "var=%%a" setlocal enabledelayedexpansion set var=!var:*:=! echo.!var! endlocal ) 短短的几行代码包含了很多技巧; 测试文本: test.txt Quote: "aou"eo ;euou%^> ::::aeui :::E2uo alejou 3<o2io| ^aue||%ou aoue eou 2 euo 8 ege#6758!7^^9098!98%$&^ ueyi^^^^aueuo2 ~ ! @ # $ % ^ & * ( () " ok " No " <>nul ege#6758^^^!7^^^^9098^!98%$&^^ ~ ! @ # $ %" ^ "& * ( () " ok " No " <>nul sdsdg:sadgs 并没有经过大量的测试;诸位帮忙测试;
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传
相似文档                                   自信AI助手自信AI助手

当前位置:首页 > 教育专区 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服