Board logo

标题: [特效代码] 批处理中的冒号消隐与标签重叠 [打印本页]

作者: cjiabing    时间: 2012-2-28 00:52     标题: 批处理中的冒号消隐与标签重叠

那么晚了,crlf还在啊,响应你号召我整理下!~
——————————————————————————————————————————————
第一、冒号隐身术
      我们通常认为,冒号在批处理中的主要作用就是标记标签,并兼做标记注释(标签文本化功能)。但在特定情况下,比如变量延迟情况下,冒号会产生一些特殊的作用。
      大家看下下面的例子,重点看“!第一个!、:第二个:、%第二个变种:”这三句,这三句都没有在批处理运行时将结果显示到窗口。其中,“!第一个!”是一个合法的变量名,因为前面没有“set  第一个=XX“,所以,这个变量名就是空的,当然不显示了。第二个可能你还说得通,把它当做一个特殊的标签,这种标签是存在的,我在其他帖子里有介绍。最关键的还是第三个“%第二个变种:”,它既不是变量名,也不是标签名,但它就是不见了,这是为什么呢?
  1. @echo off&setlocal enabledelayedexpansion&title 冒号之问!~
  2. echo;
  3. echo;
  4. echo;    1、如果你在这里看见证明你的神性比往日已经有了许多的进步……
  5. echo;    2、但我要做一件事,让你知道你的智慧仍然无法与诸神相媲美……
  6. echo;    3、!如果你已经看见我给你传达的智慧,那么我将让你大开眼界:&pause
  7. echo;
  8. echo;    4、现在,我想问你,你看见第三句了吗?
  9. echo;    5、是的,你没有仔细看,那么,我们来两个故事吧……&pause
  10. echo;
  11. set str=!我潜心向神学习智慧之道!
  12. echo;        1)!str!【%str%】为什么值为空呢?因这个变量“^!我潜心向神学习智慧之道^!”没有SET。
  13. echo;&pause
  14. echo;
  15. set var=!神啊救救我吧:
  16. echo;        2)%var%【!var!】:var:【!var:】这里又是为什么变空了呢?&pause
  17. echo;
  18. echo;    6、我们继续举一反三,请看代码和运行效果,不解释……&pause
  19. echo;
  20. echo;
  21. !这样你能看见我吗?:
  22. !第一个!
  23. :第二个:
  24. %第二个变种:
  25. echo;
  26. echo;    7、以下可能无法成功执行!~&pause
  27. echo;
  28. 第三个!
  29. 第四个:
  30. !第五个
  31. :第六个
  32. echo;
  33. pause
复制代码
这是为什么呢?我们再来看,导致我今天找出答案的例子:
  1. @echo off
  2. set "Symbol= !#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""
  3. echo;"%Symbol%"
  4. ::这里有一对百分号,所有字符显示完整。
  5. pause
  6. @echo off
  7. set "Symbol= !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""
  8. echo;"%Symbol%"
  9. ::这里去掉了一个百分号,这段“%%&'()*+,-./0123456789:”被消失了!~
  10. pause
  11. @echo off&setlocal enabledelayedexpansion
  12. set "Symbol= !#$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""
  13. echo;"%Symbol%"
  14. ::这里启用了变量延迟,两个百分号,这段“!#$%&'()*+,-./0123456789:”被消失了!~
  15. pause
  16. @echo off&setlocal enabledelayedexpansion
  17. set "Symbol= !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""
  18. echo;"%Symbol%"
  19. ::这里启用了变量延迟,一个百分号,这两个“#$”变回来了,但“%&'()*+,-./0123456789:”仍然没见。
  20. pause
  21. @echo off&setlocal enabledelayedexpansion
  22. set "Symbol= !#$%%&'()*+,-./0123456789;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~":"
  23. echo;"%Symbol%"
  24. ::如何处理?把叹号放在前面,冒号放在后面,结果,全不见了!~看来,问题就在叹号和冒号之间,在百分号与冒号之间。实际上,可能是冒号在这里扮演了一个配对变量的角色——在变量延迟的情况下,在变量名不完整的情况下,冒号会自动补上变量,使之成为真正的变量——乱猜的,。
  25. pause
复制代码
这里再引用试验代码,检查变量与标签之间的关系,检查!、%、:三者之间的关系。
  1. @echo off
  2. set eee=我爱北京天安门!
  3. !aaa:
  4. ::在变量延迟的情况下,这里也会消失。
  5. echo !bbb:
  6. echo %ccc:
  7. ::这里的变量消失了!~
  8. echo %eee:
  9. ::奇怪的是这里的eee没有消失!~看来与变量无关啊?
  10. echo %%ddd:
  11. call :!%ccc:123!
  12. pause &cls
  13. echo;
  14. call :%ccc%:123
  15. pause&exit
  16. :!123!
  17. echo;
  18. echo;    春天回来了,你也会回来吗?
  19. echo;
  20. goto :eof
  21. :123:%ccc%:123:%ccc%:123:%ccc%:123:%ccc%:123:%ccc% :123标签只是单纯的文本,并且只支持findstr /b功能,让在标签上的代码都失去了命令意义。
  22. echo;
  23. echo;    抽刀断水水更流,举杯销愁愁更愁!~
  24. echo;
  25. goto :eof
复制代码
由上可见,!var:也得,%var:也得,它们与之间存在一定的关系!~再看:
  1. @echo off
  2. set v=1234
  3. echo %!v:123
  4. pause
复制代码
其他可能与此有关的一些问题:http://www.bathome.net/viewthread.php?tid=11887
再引用10年的一个例子,我一直搜藏着:
  1. @echo off
  2. for %%i in ( 我爱,北京?天安门) do echo %%i
  3. for %%a in (8,"link":"\/index.php?mod=profile&u=c265e4bd629300c5c7f3fe500658b4b038e55da5399c45ba",-) do echo %%a
  4. pause
  5. 以逗号为分隔符
  6. 补充:
  7. 有此对网页内容进行编辑,发现一个无法读取的问题,经分析如下:
  8. 1、for %%a in ( 我爱,北京?天安门) do echo %%a
  9. 2、for %%a in ( 我爱,北京天安门) do echo %%a
  10. 上例无法显示,下例可以正常显示。有此可知,英文状态下的问号在字符串中有通配符的意义,但干嘛跑这里来意义呢?
复制代码
第二、标签的范围与标签重叠
    标签可能是一个区功能化的文本区,因为放在标签中的任何字符(><另外再考虑)都不会发生命令功能,也就是,你在标签中插入一个del命令,它根本不执行。另外,crlf提示,一个完整的标签功能区是(0,512)的范围,超出这个范围就是另起一行,因此,我们可以用其他字符填满标签511个字符,然后紧接着后面513位置接其他命令,这时,命令可以执行了,这正说明了标签的范围大小。由以下例子可知,标签虽然是长长的一行,但实际上在达到512时标签已经自动换行了,所以,此时命令可以接上去。
  1. cls&title 标签之问!~:: by cjiabing from 批处理之家 http://www.bathome.net&title 批处理标签重叠大法
  2. echo;
  3. call :1111
  4. ::这是正常标签引用
  5. pause
  6. echo;
  7. call :2222
  8. ::在标签前后添加了一些字符,但标签仍然发生作用
  9. pause
  10. echo;
  11. call :3333
  12. ::你会发现,原来标签的末尾真的有尾巴,这个尾巴是一行511字符,513字符后是另外一行——可以拼接另外一组命令,甚至另外一个标签。
  13. pause
  14. echo;
  15. echo;
  16. set 4444=511513
  17. echo;
  18. call :4444
  19. ::演示稍微复杂的标签与命令的连接。
  20. pause
  21. echo;
  22. echo;
  23. set num=0
  24. call :5555
  25. ::在这一长长的行中,你会发现两个标签,一个是:morel,另外一个是:look,这里先执行第一个标签。
  26. pause
  27. echo;
  28. echo;
  29. echo;     ★  注意:这是第二次运行!~
  30. echo;      
  31. call :6666
  32. ::为了证明这个是第二个标签,特地在前面加了个识别“try”,你会看到,第二个标签引用的段落与第一个是一样的。
  33. pause
  34. echo;
  35. echo;      如果没看清楚我们再来一遍!~
  36. echo;
  37. pause&cls
  38. call :5555
  39. pause
  40. call :6666
  41. pause
  42. exit
  43. :1111
  44. echo;
  45. echo;    一、我爱批处理
  46. echo;
  47. goto :eof
  48. :====2222 iloveyoubaby
  49. echo;
  50. echo;    二、请注意观察标签“2222”前后。
  51. echo;
  52. goto :eof
  53. :3333  adiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyou dir/b
  54. echo;
  55. echo;    三、请找到dir命令所在的位置!~
  56. echo;
  57. goto :eof
  58. :4444     iloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubayiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyix[!511!]echo;Hello&for /l %%a in (1,1,10) do echo %%a
  59. echo;
  60. echo;     四、可以在标签末尾【511列之后】放置命令,命令可以组合。
  61. echo;         注意空格不能少,中文可能不支持。
  62. echo;
  63. goto :eof
  64. :5555 iloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirlovde     echo;first        :6666  iloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyiloveyoubabyiloveyoubabydirloveyoubabyilove
  65. echo;
  66. set /a num+=1
  67. echo;     五、两个标签共一行,执行两次,现在第 【!num!】 次!~
  68. echo;
  69. echo;         标签重叠——见过一行里有两个以上的标签吗?
  70. echo;         本段代码有两个标签:5555和:6666,而且每个标签都会发生作用!~
  71. echo;         从此以后,你可以大声说,你也可以用一行两个标签来执行命令了!~
  72. echo;
  73. echo;
  74. goto :eof
复制代码
这个标签不是简单地接就可以了,你可以在第一个标签的最后加一个命令,然后下一个标签,又接一个命令……如:
      able1 …………511  echo;hello& :Lable2 …………1030 echo;I am here & Lable3…………
      这样,你就可以把所有命令拼接在一行里实现,即使有标签都可以如此制作。
作者: CrLf    时间: 2012-2-28 03:07

标签被调用时,一次性只读取 512 字节这个倒是第一次知道,看来预处理中还有不少彩蛋,学习了
作者: CrLf    时间: 2012-2-28 03:37

顺便说一下,那个 !str: 其实是...以前在贴吧看到有人问,还是发个链接吧:
http://tieba.baidu.com/p/1020906001
注:此贴中一头猪是扮猪,还有一个美女是斑竹,一只猫是吧猪,剩下两个没有脸的都经常发炎,尤其是那个头像比较绿的人技术很好...
嗯,你懂得,我什么都没说。
作者: cjiabing    时间: 2012-2-28 06:03

回复 3# CrLf


    你好幽默呢!~整理了一下,有许多想说的,又有许多想试验的,天亮了再说!~
作者: ivor    时间: 2012-2-28 09:45

细看才发现这不就是变量替换吗?set /?里面的功能
作者: powerbat    时间: 2012-2-28 19:25

http://www.bathome.net/thread-3768-1-1.html
从中可以看出预处理时对百分号%的一种处理机制:首先是百分号%被消去,然后看%后面的字符之后有无冒号“:”,如果没有,%后面的字符被保留。如果有冒号“:”,则看%与冒号“:”之间的字符是否为已经定义的变量名,如果是变量名,则此变量名与冒号“:”都会被保留;如果不是,则%与冒号“:”之间的字符包括冒号“:”都被消去,只保留冒号“:”之后的字符。
在开启变量延迟的情况下,是用!代替%,预处理时对其处理方式与%相同。

转自verybat,其实还有一篇文章就是专门讲%与:的关系。
verybat对变量机制的研究非常深入透彻,当时xzyx、zqz0012005等人写了很多理论分析的文章,可惜verybat突然关闭了,都没来得急收藏。
作者: cjiabing    时间: 2012-2-28 23:00

回复 6# powerbat


    描述是准确的,但仍然没有揭示原因,所以不能说是深刻。
    第一个冒号的作用我仍然不明就里,第二个标签还是比较好理解的。
    这个问题稍微复杂一些,我一时半会描述不清楚,大家一起讨论了。
作者: qzwqzw    时间: 2012-3-21 18:15

确实讨论的问题有点多且杂了
百分号和冒号的问题确实如5楼所说
是变量扩展时的替换和截取特性所带来的问题
setlocal DISABLEEXTENSIONS禁用了命令行扩展之后
这个问题就没有了
当然会带来其它一些问题

10年前的那个案例
记得曾有专贴讨论过
在for的遍历集里出现? 和 *
必然会被当作文件集进行匹配
因为它们是文件通配符
否则被当作普通字符串进行匹配
这个特性似乎没什么好争议的




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