Board logo

标题: [分享]从一个有趣的结果来谈谈批处理变量嵌套 [打印本页]

作者: batman    时间: 2009-11-14 00:45     标题: [分享]从一个有趣的结果来谈谈批处理变量嵌套

请大家先运行下面的批,然后带着疑问和我一起来学习下变量嵌套:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,5) do set "str%%a=%%a"&!set "str=!str! ^!str%%a^!"
  3. echo %str%
  4. echo !str!
  5. pause>nul
复制代码
为什么会这样?肯定很多人已经产生了这样的疑问。按照平常我们对变量
理解,%str%和!str!出来的结果应该是一样的,因为同是str变量,唯一的只
是存在着变量符%和!的区别嘛。呵呵,大家可别小看了这个变量符的变化了,
这里面可是大有文章的哦。。。
    我们先来分析下这段代码,第一句不用多说是关回显开变量延迟,第二句
中就存在着技术成分了,利用for循环依次赋值给变量,在这其中每次对两个变
量进行了赋值,以%%a值为1时为例:set "str1=1"将变量str1的值定义为1,而
set "str=!str! ^!str%%a^!",大家特别要注意^!,利用^将!转义为如同a b c
一样的字符,解析出来就是set "str= !str1!"(初始时str值为空,!为字符),
这样当%%a值为 2时,str2变量的值被定义为2,而str的值变成了 !str1! !str2!
,最后当%%a值为5时,str5变量的值被定义为5,str的值因为五次的累加赋值变
成了 !str1! !str2! !str3! !str4! !str5!,整个for循环结束。
    于是当我们echo !str!时,这时str变量的变量符!触发变量延迟作用,cmd进行
预处理,解析出其值是个变量集合,经过预处理后,cmd不会再进一步解析其
值中每对!!间的变量了,而当我们echo %str%时,变量str的值首先被解析出
来,这时其值中的!触发了变量延迟,cmd进一步对值中每对!!中的变量进行解
析,于是输出了最终的结果 1 2 3 4 5。
    我们再回过头来看一下这到底是怎么一个概念?其实这就是变量嵌套,代码中
的str变量实际上是一个表达式,它代表的是一个由str1到str5变量所组成的集
合,而这个集合中的变量只有在!变量符触发变量延迟的情况下,其值才会被解
析出来。
    好了,今天就讲这么多了,下面就再给大家一个变量嵌套的例子,帮助大家
好好理解所讲到的内容:
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (1,1,3) do (  
  3.     if not defined str1 set "str=!str!^!str1^!"  
  4.     set "str1=%%a"
  5.     for /l %%a in (1,1,3) do (
  6.         if not defined str2 set "str=!str!^!str2^!"  
  7.         set "str2=%%a"
  8.         for /l %%a in (1,1,3) do (
  9.             if not defined str3 set "str=!str!^!str3^!"  
  10.             set "str3=%%a"
  11.             for %%a in (!str!) do echo %%a
  12.         )
  13.     )
  14. )
  15. pause>nul
复制代码

[ 本帖最后由 batman 于 2009-11-14 01:22 编辑 ]
作者: Seter    时间: 2009-11-14 15:47

BATMAN还真是...凌晨还在写教程,好敬业啊!
变量嵌套这东西...唉还真不是一天两天就能弄明白的,我还得努力啊
作者: gxuan2008    时间: 2010-6-23 20:38

似懂非懂。
set 命令里!str!不是变量吗?
呵呵。从上面运算的结果来看应该是不算的。
作者: leap    时间: 2010-10-16 16:56

转自该处:http://club.topsage.com/thread-1729220-1-1.html
一,为什么要使用变量嵌套?
1,可提高代码的效率
2,可以缩减代码
二,批处理变量的种类
1, %0~%9 , %*:   批处理运行的命令行参数,在代码行预处理时扩展,可视为最高优先级扩展。

  如果存在变量:
12a=###
!a=$$$
a=aaa

echo;%12a%
得到结果为:第一个参量+2a

echo;%!a%!
得到结果为:$$$

2, %var%: 在代码行预处理时扩展,其实与%1~%9 ,%*是同时扩展取值的,
  但是%1%得到的只有可能是参量值,绝不可能得到变量值,所以视为次优先级扩展,

********** 预处理问题 **********
  要很好的理解%号变量,需要知道一点,预处理一次是读取一组来进行的  
    如:

      [1]
      set a=aa&echo %bb%

      [2]
      if aa==%bb% (
              echo 等
      )

      [3]
      (
              echo %aa%
              echo %bb%
      )
      上面就是所“组”的形式。

    还要知道:%号变量在预处理读取代码行时已经扩展了
      例如:

因为%kh%扩展了,其值中的 ")" 与前面的 "(" 形成了一组代码,其实只读取了一行,
      运行代码遇到pause暂停后,修改bat文件, 把echo bb改为:echo cc,并保存后,再按键继续可见到显示的是"cc"
********** 预处理问题 **********

3, for()变量: %%a %%1 %%中 等形式均可使用,

  虽然 %%a%或者%%1 等形式得到的还是for 变量的值,而不是作为:%a% %1 扩展,
  为什么呢?
  因为预处理在处理%var%形式变量时,是从左到右以两个%号作为一组变量的,而以%+一个数字作为一组参量的,
  还有一点就是如果一组%号的两个%号间没有其它字符时,将留下一个%号,
  所以是要将for()变量视为第三优先级,
  另外变量%var% 及%1扩展后的值里面,如果含有有效的for ()变量%a的话,该值中的%a 会得到for()变量再次扩展
  更说明了for()变量要比%var% %0~%9,%*要低一级
  这个例子是变量的嵌套扩展的一种形式,
  其实for ()的变量名应该是:经预处理后的一个 %+一个字符,以cmd批处理脚本里面,可以是数字,字母,或字符和汉字都可以。
  for变量仅在该组for()内有效,并向下一级子for ()继承
  如果父级for()与子级for()有相同的有效变量时,得到的是当前级的扩展值
例如:

4,!var!: 变量延迟打开时有效,在命令运行时扩展,可视为四级优先级,
为什么要这么认为呢?
  如果for()变量扩展后,重新组成的表达式中,含有!号的话,命令运行时会再次进行!var!的取值扩展
这也是变量嵌套扩展的一种形式
   !var!变量,在一对setlocal ... 和endlocal内有效,并向下一级子环境继承

5,set/a 运算命令变量,这可以说最低级的变量扩展了,就连!var!变量也扩展后,
   如果得到的是以非数字及非运行符开头的话,就会再次进行变量扩展之后才执行运算。

三、只有了解变量扩展的优先级,才可能理解变量嵌套的用法。
  变量嵌套扩展的方式有:
*** 注意:要开启变量延迟,否则!var!变量无从谈起 ***
  1,先%1或%var% 再 !var!
只要%1或%var%扩展的值中含有或和旁边的字符组合后含有:!var!,!var:n=m!,!var:~2,2! 等形式
就会再次进行!号变量的扩展
      例如:
2,先%1或%var% 再 for ()%a
只要%1或%var%扩展后的值里面,含有有效的for ()变量%a的话,该值中的%a 会再次得到for()变量扩展
      例如:
3,先for()%a 再!var!
只要for()%a扩展的值中含有或和旁边的字符组合后含有:!var!,!var:n=m!,!var:~2,2! 等形式
就会再次进行!号变量的扩展,这也就是为什么开启了变量延迟后,会丢失!号的原因。
      例如:

4,先%1或%var% 再 for ()%a 再!var!
也就是第2+第3种用法的

      例如: 5,多级嵌套:先%1或%var% 再 for ()%a 再!var! 再for ()%b 再!var!
主要是for ()%a 和!var!间的循环嵌套,看例子: ** 这点是变量嵌套的最巧用法,是提高批处理脚本的关键 **
  6,运算命令变量,就是在其它变量都扩展完了之后,还可以再进行扩展一次。
代码中:%c% => !b!*2 => a*2 => 100*2 所以结果进行了以下运算:
  1. set /a d=100*2+10
复制代码

7,管道方式:

set aa=d 100 &((echo;%%aa%%&echo;q)|debug)

虽然是在同一行,也没有用到call ,但是%%a%%扩展成了d 100
原因是(echo;%%a%%&echo;q)是在批处理外运行的,并且通过管道把显示值传给后面的命令debug 作为输入行。

  8,效率较低级的call 方式变量嵌套,相当于从第1种方式到第5种方式再循环一次。(有所重复,就不详述)

四、变量嵌套扩展的实际应用。
  1,if 用法
  1. set "str= help open quit run "
  2. set in=输入:
  3. if "!str: %in% =!" neq "!str!" goto :m_%in%
  4. :m_help
  5. :m_open
  6. :m_quit
  7. :m_run
复制代码

2,for 用法
  1. set n1=姓名:!@n!    性别:!@xb!
  2. set n2=学历:!@xl!    年龄:!@g!
  3. set n3=民族:!@m!    体重:!@tz!
  4. ::前面先不要开变量延迟
  5. setlocal enabledelayedexpansion
  6. for /f "tokens=1-6" %%1 in ("benton 男 中专 18岁 汉 90kg") do (
  7. set @n=%%1&set @xb=%%2&set @xl=%%3&set @g=%%4&set @m=%%5&set @tz=%%6
  8. )
  9. for /l %%a in (1,1,3) do (
  10. for %%b in (!n%%a!) do (echo;%%b)
  11. )
  12. set n &rem 查看变量方便理解
复制代码

[ 本帖最后由 leap 于 2010-10-16 17:01 编辑 ]




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