Board logo

标题: 批处理变量延迟详解 [打印本页]

作者: Batcher    时间: 2008-12-25 14:39     标题: 批处理变量延迟详解

因工作关系一直没有太多时间泡在论坛上,每次上本论坛都很匆忙,注册几个月以来第一次发贴(因为本人太菜,不敢发帖),真有点不好意思!希望大家多多支持!
本帖只“照顾”新手,老鸟略过,哈!

以下是偶根据论坛内的帖子得出的结论,如有错漏敬请指正!
首先要特别感谢willsort老大写的这帖子,偶是从中得到启发的!
http://www.cn-dos.net/forum/viewthread.php?tid=20733

下面在讨论过程中,偶会插入一些“废话”,如果你不喜欢看我写的“废话”,可以跳过。它与本文讨论的中心完全无关!不过偶还是建议大家看看,哈!

这段可跳过:

同学们上课啦,第一天来这里任教,必须先做个自我介绍。偶叫金城武,啊啊~~~~~~不,不是,一时口快说错了, 偶姓贾,不好意思!由于近日忙于研究“鬼武者”,所以忘了自己的姓氏!什么,说我现在才玩鬼武者,哎!没办法啊,偶穷啊,口袋里总是掏不起这几块钱买啊。“几块钱?”众人议论纷纷。什么,又说我买盗版,啊~~啊~~~这~~这不在本文的讨论范围啊~~~~~说完台下众人举起砖头……^_^

这是正文不可跳过:

willsort老大上面的帖子,对于新手来说比较难理解。不过没关系,我们先分析一个例子,同样是引用willsort老大的。本例启用了变量延迟,是个正确的例子!
例1:
  1. @echo off & setlocal EnableDelayedExpansion
  2. for /f  "tokens=* delims=" %%i in ("Hello world.") do (
  3.     set n=%%i
  4.     set n=!n:ld.=t!
  5.     set n=!n:o w= S!
  6.     set n=!n:He=Wi!
  7.     echo !n!
  8. )
  9. 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 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:
  1. @echo off
  2. for /f  "tokens=* delims=" %%i in ("Hello world.") do (
  3. set n=%%i
  4. set n=%n:ld.=t%
  5. set n=%n:o w= S%
  6.     set n=%n:He=Wi%
  7.     echo %n%
  8. )
  9. 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:这是个错误的例子
  1. @echo off
  2. set mm=girl&echo %mm%
  3. 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命令就显示什么值。
大家这样做就明白了:
  1. @echo off
  2. set mm=boy
  3. set mm=girl&echo %mm%
  4. pause
复制代码
看看显示什么结果就知道了!

这样编写例3才正确:
  1. @echo off&setlocal EnableDelayedExpansion
  2. set mm=girl&echo !mm!
  3. pause
复制代码
开启了变量延迟,变量扩展(替换)的行为就推迟到echo命令执行时,这时echo能感知它前面的命令(本例的set)对变量mm做了什么“坏事”,从而作出正确的判断并执行

好了全篇完了,下课!

突然,门外传来几只“恐龙”的嚎叫声:把那小子抓回去,胆敢趁着节日咱们游山玩水的时候偷走!!
2分钟后,“恐龙战队”押着这小子来到一个美丽壮观的“城堡”面前,正门上方写着“XX疯人院”,哈哈!

愿天下美女妇女节快乐,一天比一天美(包括在浏览本贴的你)!!---汗,这里有女同胞吗??有的请举手!呵呵!39们也一起感受节日的气氛吧!
这帖本来是想上午发的,但因工作关系,到现在才有空,无奈啊!
以上这些“废话”只是想令大家阅读这贴时能增添几分气氛,增加大家的阅读兴趣,令大家在学习过程中轻松轻松而已。如有得罪,敬请批评指正!

原文地址:http://www.cn-dos.net/forum/viewthread.php?tid=28273
作者: xujin1402008    时间: 2008-12-28 15:18

好长的贴啊,要仔细研究下~
作者: ahongguo    时间: 2008-12-28 16:14

个人见解,如有冒犯请间量(俺是新手)  这种延迟好象作用不大
对于你上所举例子,好象与for  set 读写机智有关呢  如:
A)@echo off
for /f  "tokens=* delims=" %%i in ("Hello world.") do (
set n=%%i
set n=%n:ld.=t%
set n=%n w= S%
    set n=%n:He=Wi%
    echo %n%
)
pause
将 echo %n%   弄到那括号外面,就可以显示了
B)
@echo off
set mm=boy
set mm=girl&
pause
将后面的 echo %mm%  另起一行 同样能达到那种所要目的
作者: yslyxqysl    时间: 2008-12-28 16:35

  1. @echo off&setlocal EnableDelayedExpansion
  2. set a=2
  3. set b2=x
  4. if !b%a%!==x echo yes
复制代码
这个批处理你不开延迟吗?
作者: Batcher    时间: 2008-12-28 19:58     标题: 回复 3楼 的帖子

变量延迟扩展的作用很大,建议多看几遍相关教程。
作者: lzwudi    时间: 2009-1-4 11:49

非常理解您的心意...谢谢.但是给个意见.请勿见怪!
个人认为您的意图很明确.但是文章不是很通熟易懂......
就像你提到的"willsort老大"...说"其粗劣的翻译水平"......
他的语言表达能力也不怎么理想....
还有像"ahongguo"所说的一样...您举的几个例子好象起不到启发的作用....
在我这样的新手看来.变量延迟的意义懂了点.但是按照您的例子学习..以后应用是点问题
这样的例子是不需要用变量延迟就可以达到的..所以用它来举例有点迷惘
作者: Batcher    时间: 2009-1-4 13:07     标题: 回复 6楼 的帖子

//就像你提到的"willsort老大"...说"其粗劣的翻译水平"......

不知这位仁兄在哪里看到有人说“willsort老大粗劣的翻译水平”?

//他的语言表达能力也不怎么理想....

关于变量延迟扩展的教程,本版有好几篇,建议结合起来看。

//还有像"ahongguo"所说的一样...您举的几个例子好象起不到启发的作用....

变量延迟扩展绝对不是看几个例子就能掌握的,需要今后自己写代码的过程中慢慢体会。

//在我这样的新手看来.变量延迟的意义懂了点.但是按照您的例子学习..以后应用是点问题

哪里有问题就问哪里。能发现自己的问题所在也是一直进步。

//这样的例子是不需要用变量延迟就可以达到的..所以用它来举例有点迷惘

如果你是说3楼的帖子,建议重新看几遍教程。因为3楼的兄弟根本没有理解变量延迟扩展应该用在什么地方。
作者: cmbatd    时间: 2009-1-4 17:00     标题: 回复 4楼 的帖子

郁闷~~
刚才的回复居然没有发上去
换行就行了,不过楼主讲的貌似不是为个主题
我是新手,刚加进来的
看了很受益!!!
作者: nanoking    时间: 2009-1-5 03:23

似乎只要引用了!var!,都要加变量延迟。
作者: zjw767676    时间: 2009-1-23 13:49

多看几个教程,反复练习,会有懂的一天的
作者: jackelijie135    时间: 2009-3-15 16:29

有点收获,O(∩_∩)O哈哈~
作者: januapr    时间: 2009-8-8 05:13

这是一篇不错的文章,看了几次受益不浅!
作者: fuwei880306    时间: 2009-12-16 21:02

越看越觉得玄了..似懂非懂的样子
作者: changedirectory    时间: 2013-12-11 15:10

回复 3# ahongguo


    如果把echo %n%放到外面,显示的结果就是He=Wi




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2