[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
@echo off
for /f "delims=" %%a in (
file1
file2
file3
) do echo [%%a] xxx
rem 如果某个文件找不到,不再遍历后面的文件。
pause

for /f "delims=" %%a in (
"
aaa
bbb
"
) do echo [%%a] xxx
echo 111&pause

for /f "delims=" %%a in (
'
echo aaa
echo bbb
echo ccc
'
) do echo [%%a] xxx
echo 222&pause

多行模式中,换行被预处理成了空格。
蓝色的单双引号位置可以随意,但红色的双引号与反括号不能在一行。

TOP

本帖最后由 powerbat 于 2012-3-17 23:19 编辑
@echo off
for /f "delims=" %%a in (
"
aaa
bbb
"
) do echo [%%a] xxx
echo 111&pause

for /f "delims=" %%a in ("
aaa
pause
") do echo [%%a] xxx
"
) do echo [%%a] xxx
pause
echo 222&pause


去掉echo off,可以看到预处理后变成了for /F "delims=" %a in (" aaa pause ") do echo [%a] xxx ") do echo [%a] xxx

当"与反括号在同一行时,for以为这是字符串的一部分,在后面如果找不到for语句的逻辑组成部分“) do ”,就会报错。

TOP

本帖最后由 qzwqzw 于 2012-3-19 14:37 编辑

回复 11# CrLf

“莫名奇妙”
未从代码中观察到任何异常的情况发生
倒是注意到“||(echo !a!&”似应是"||(echo !,!&"
也许是环境问题
Microsoft Windows XP [版本 5.1.2600]

usebackq的内存泄漏问题也是早前曾见过
后来想找的时候反而找不到了
给一段Google到的代码
  1. @echo off
  2. for /f "tokens=4 usebackq" %%a in ('a b c d e f g h') do echo %%a
  3. for /f "tokens=4 usebackq" %%b in ('a b c d e f g h') do echo %%b
  4. for /f "tokens=4 usebackq" %%c in ('a b c d e f g h') do echo %%c
  5. for /f "tokens=4 usebackq" %%d in ('a b c d e f g h') do echo %%d
  6. for /f "tokens=4 usebackq" %%e in ('a b c d e f g h') do echo %%e
  7. for /f "tokens=4 usebackq" %%f in ('a b c d e f g h') do echo %%f
  8. for /f "tokens=4 usebackq" %%g in ('a b c d e f g h') do echo %%g
  9. for /f "tokens=4 usebackq" %%h in ('a b c d e f g h') do echo %%h
  10. for /f "tokens=4 usebackq" %%i in ('a b c d e f g h') do echo %%i
  11. for /f "tokens=4 usebackq" %%j in ('a b c d e f g h') do echo %%j
  12. for /f "tokens=4 usebackq" %%k in ('a b c d e f g h') do echo %%k
  13. for /f "tokens=4 usebackq" %%l in ('a b c d e f g h') do echo %%l
  14. for /f "tokens=4 usebackq" %%m in ('a b c d e f g h') do echo %%m
  15. for /f "tokens=4 usebackq" %%n in ('a b c d e f g h') do echo %%n
  16. for /f "tokens=4 usebackq" %%o in ('a b c d e f g h') do echo %%o
  17. for /f "tokens=4 usebackq" %%p in ('a b c d e f g h') do echo %%p
复制代码
for/r倒是没发现内存泄漏问题
你是指for/r+ren时的文件名反复修改的问题?
或者是指for/r文件名解析的问题?
http://bbs.bathome.net/viewthread.php?tid=7629
http://bbs.bathome.net/viewthrea ... amp;page=1#pid29165
天的白色影子

TOP

回复 18# qzwqzw


变量名确实写错了,copy 测试文本时漏改一个。
xp 没有出现问题吗?win7 和 vista 下测试过,变量 !,! 确实能存放 8190 个字符长度的变量,而普通非分隔符 变量名则只能存放 8189 个,debug 部分见附文:
for /r 有问题是 10 楼说的呀...是否 for /f 之误?

附:
昨日的猜测有误,再次尝试 debug,全程如下:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /l %%a in (0 1 8192) do (
  4.   set,=a!,!||(echo !,!&echo 长度为 %%a 个字符&debug&exit)
  5. )
  6. rem 将原测试代码改为达到上限时执行 debug,以便手动观察内存内容
复制代码
以下为进入 debug 后的键入的命令及输出:
  1. -d2cl2
  2. 1EEC:0020                                      51 07
复制代码
可知变量表的数据段地址为 0751
  1. -s751:0 lffff ",=aaa"
  2. 0751:0028
复制代码
得知 !,! 变量在变量表中始于 0028 偏移地址
  1. -h 28 1fff
  2. 2027  E029
复制代码
得知相对于 0028 偏移 1fff(即十进制 8191)的地址为 2027
  1. -d751:20
  2. 0751:0020  41 4E 44 2E 43 4F 4D 00-2C 3D 61 61 61 61 61 61   AND.COM.,=aaaaaa
  3. 0751:0030  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  4. 0751:0040  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  5. 0751:0050  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  6. 0751:0060  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  7. 0751:0070  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  8. 0751:0080  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  9. 0751:0090  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  10. -d751:2020
  11. 0751:2020  61 61 61 61 61 61 61 61-42 4C 41 53 54 45 52 3D   aaaaaaaaBLASTER=
  12. 0751:2030  41 32 32 30 20 49 35 20-44 31 20 50 33 33 30 20   A220 I5 D1 P330
  13. 0751:2040  54 33 00 00 01 00 43 3A-5C 57 49 4E 44 4F 57 53   T3....C:\WINDOWS
  14. 0751:2050  5C 53 59 53 54 45 4D 33-32 5C 44 45 42 55 47 2E   \SYSTEM32\DEBUG.
  15. 0751:2060  45 58 45 00 51 8C 56 1E-E8 2C 00 72 17 8B F7 E8   EXE.Q.V..,.r....
  16. 0751:2070  4D 00 00 5F 07 80 3C 00-75 01 4E E8 70 01 2B CE   M.._..<.u.N.p.+.
  17. 0751:2080  06 1F F3 A4 1F 5E C3 BE-4C 8C EB 03 BE 51 8C E8   .....^..L....Q..
  18. 0751:2090  05 00 72 F2 E9 9C 00 FC-E8 41 00 8E 06 1A 96 26   ..r......A.....&
复制代码
观察变量 !,! 头尾内容,发现末尾处的 00 丢失了,并且其后紧随着一串陌生的变量变量内容,于是在批处理中写入如下代码在内存中搜索,结果存于 s.txt
  1. (
  2. (
  3. for %%a in (0 1 2 3 4 5 6 7 8 9 a b c d e f) do @echo s %%a000:0 lffff "BLASTER=A220 I5 D1 P330"
  4. )
  5. echo q
  6. )|debug>s.txt
复制代码
s.txt 的内容:
  1. -s 0000:0 lffff "BLASTER=A220 I5 D1 P330"
  2. 0000:0C7D
  3. 0000:4522
  4. 0000:53F7
  5. 0000:59B7
  6. 0000:5A92
  7. 0000:5BA2
  8. 0000:A568
  9. 0000:B1D7
  10. 0000:B562
  11. -s 1000:0 lffff "BLASTER=A220 I5 D1 P330"
  12. -s 2000:0 lffff "BLASTER=A220 I5 D1 P330"
  13. -s 3000:0 lffff "BLASTER=A220 I5 D1 P330"
  14. -s 4000:0 lffff "BLASTER=A220 I5 D1 P330"
  15. -s 5000:0 lffff "BLASTER=A220 I5 D1 P330"
  16. -s 6000:0 lffff "BLASTER=A220 I5 D1 P330"
  17. -s 7000:0 lffff "BLASTER=A220 I5 D1 P330"
  18. -s 8000:0 lffff "BLASTER=A220 I5 D1 P330"
  19. -s 9000:0 lffff "BLASTER=A220 I5 D1 P330"
  20. 9000:5F02
  21. 9000:FFA6
  22. -s a000:0 lffff "BLASTER=A220 I5 D1 P330"
  23. -s b000:0 lffff "BLASTER=A220 I5 D1 P330"
  24. -s c000:0 lffff "BLASTER=A220 I5 D1 P330"
  25. -s d000:0 lffff "BLASTER=A220 I5 D1 P330"
  26. -s e000:0 lffff "BLASTER=A220 I5 D1 P330"
  27. -s f000:0 lffff "BLASTER=A220 I5 D1 P330"
  28. -q
复制代码
用 d 逐个查看,找到可疑的地址为 9000:5F02
  1. -d9000:5F02
  2. 9000:5F00        42 4C 41 53 54 45-52 3D 41 32 32 30 20 49     BLASTER=A220 I
  3. 9000:5F10  35 20 44 31 20 50 33 33-30 20 54 33 0D 74 2E 65   5 D1 P330 T3.t.e
  4. 9000:5F20  78 65 0D 61 61 61 61 61-61 61 61 61 61 61 61 61   xe.aaaaaaaaaaaaa
  5. 9000:5F30  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  6. 9000:5F40  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  7. 9000:5F50  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  8. 9000:5F60  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  9. 9000:5F70  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  10. 9000:5F80  B4 0E   
复制代码
果然有关联,但怎么有个 0d 呢?而且 BLASTER=A220  等内容既然是正序排列,为何位于 !,! 内容之前?
用 d9000:5e00 l140 继续观察前后内容(之前没发现,原来刚才那段段数据前面还有内容,汗,那就不是堆栈的原因了),发现这里似乎和变量表的内容不同
  1. -d9000:5e70 l120
  2. 9000:5E70  8C 16 30 00 2E 8E 16 57-22 BC A2 07 E8 80 31 8A   ..0....W".....1.
  3. 9000:5E80  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  4. 9000:5E90  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  5. 9000:5EA0  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  6. 9000:5EB0  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  7. 9000:5EC0  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  8. 9000:5ED0  61 61 61 61 61 61 61 61-61 61 61 61 00 20 20 20   aaaaaaaaaaaa.
  9. 9000:5EE0  20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20           .....
  10. 9000:5EF0  20 20 20 20 20 20 20 20-00 00 00 00 61 61 61 61           ....aaaa
  11. 9000:5F00  00 0D 42 4C 41 53 54 45-52 3D 41 32 32 30 20 49   ..BLASTER=A220 I
  12. 9000:5F10  35 20 44 31 20 50 33 33-30 20 54 33 0D 74 2E 65   5 D1 P330 T3.t.e
  13. 9000:5F20  78 65 0D 61 61 61 61 61-61 61 61 61 61 61 61 61   xe.aaaaaaaaaaaaa
  14. 9000:5F30  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  15. 9000:5F40  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  16. 9000:5F50  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  17. 9000:5F60  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  18. 9000:5F70  61 61 61 61 61 61 61 61-61 61 61 61 61 61 61 61   aaaaaaaaaaaaaaaa
  19. 9000:5F80  B4 0E CD 21 2E 8E 1E 1A-96 B8 FF FF 87 06 B4 02   ...!............
复制代码
得出的结果实在让我费解,为何这里也多了个 00 0d...
反复试验了一下,和我昨日的猜测不一样,似乎即使变量长度达不到 8190 也能看到同样的内存数据,那究竟是何处发生数据溢出的呢?
昨天没有想周全,所以当时的猜测不正确,看来仍然是个谜(对我来说)

TOP

回复 18# qzwqzw


    win7 下测试,这段代码显示 16 个 d(一个一行),似乎没有出现问题。
   对 useback 的 bug 向来是只闻其名不见其人(也许是用得少吧),刻意寻找也不知从何下手,看来 for /f 的幽灵很狡猾啊

TOP

回复 17# powerbat


    for /f ... %%a in ("...")do ....
不知道其他系统是什么状况,
我的Microsoft Windows XP [版本 5.1.2600]
圆括号里的双引号若隔行有异常产生(“不到文件”),是否产生异常和双引号的内容有关
SOS --- >> lllsoslll@163.com

TOP

关注中,学习中

TOP

回复 21# lllsoslll


    17楼的代码在win7中运行正常。

TOP

回复 19# CrLf
18楼的for /f 的bug代码已经在本地测试过了
发生概率是100%,下面是测试效果

for/r确实是for/f的笔误

11楼代码的问题确实有
是我没有注意到
大概分析了下
可能是这样的原因

首先cmd的命令行长度限制是8192个字符
也就是说命令行缓冲区定义的长度是8192个双字节
排除换行符和空字符有效字符是8190个
而环境变量的长度限制8192字符只体现在变量扩展和显示时
在设置时它的变量长度收到命令行缓冲区长度的限制

其次启用变量延迟后
将不在命令行缓冲区存储环境变量的扩展结果
而采用另外一个词法分析缓冲区
这个缓冲区大概是2*8192个双字节的长度
以便于字符串的切分、合并操作的展开

再次cmd支持命令和参数开关之间
除了空格、TAB等普通分隔符之外
也可以有其它的分隔符比如 : . + / [ ] " 等
与普通的分隔符不同
这些分隔符只用于切分命令和参数
不用于参数之间的切分
是cmd为了兼容旧DOS时代的一些用法而做的hack措施
而且它们在词法切分后不会被剔除
所以它们既是分隔符又是操作符
因此在做了命令与参数切分之后
参数串中仍然保留该字符

第四set命令的参数中
很多常见的命令分隔符都会发生转义
TAB、空格、分号、逗号等成为普通字符
等号除了第一个是操作符之外其它也成为普通字符
因此set后的逗号在分隔了set命令和参数之后
继续作为set的参数之一——用作变量名
天的白色影子

TOP

回复 24# qzwqzw


    感觉似乎有道理,但是不知老兄得出的这些结论有何依据?还是仅仅为个人推测呢?没有冒犯的意思,只是想确保论点的“地基”是稳固可靠站得住脚的。

TOP

回复 25# CrLf
这只是对cmd命令行分析过程中的半成品
有些不太确认的结论
我会加注意“可能”、“大致”、“也许”等类似的字样
还有些结论因为需要深入分析和多方面确认
包括set对, / + 等分隔符处理上的不同
所以暂时还没有办法贴出来
天的白色影子

TOP

回复 26# qzwqzw


    大工程啊,建议开个专题来论证,最好能深入浅出讲解一番再附上具体的验证流程,这些溯本追源的命题无论是用于加深本质性的理解还是为日后重写 cmd 提供扎实的理论依据都很有必要

TOP

本帖最后由 CrLf 于 2012-4-2 00:51 编辑

回复 8# CrLf


zqz 给出了一个帖子的链接:http://www.bathome.net/viewthread.php?tid=3614
点进去一看,觉得很有意思,我这才知道 xp 和 win7 对于 for /f 中输入参数的解析方式是不同的,于是用代码测试看二者差异何在。在此不作长篇大论,概括性地总结一下个人理解:

首先,无论在 xp 还是 win7 下,for /f 的特性都是当参数以设备开头时将触发以类似 shift 的方式对参数挨个解析
  1. set str="bbs.bathome.net" "t e s t ?" "abc*"
  2. for /f "delims=" %%a in (nul %str%) do echo 第一节为:%%a
  3. rem win7 下,顺序解析将终止于用户所指定的最后一个参数或所遇见的第一个字符串参数再或者发生找不到指定文件的错误,所以 win7 下可以用非遍历的方法获取第一节的字符串——虽然比遍历方案方便,但是当然不如以双引号为分隔符来得直接,所以这是个没什么价值的方案
复制代码
  1. set str="bbs.bathome.net" "t e s t ?" "abc*"
  2. for /f "delims=" %%a in (nul %str:" "=" @"%) do echo %%a
  3. rem 而在 xp 下,顺序解析不会因为遇到字符串参数而终止,但是对下一个参数进行解析时,会诡异地吞掉该参数的首字符,所以此处要加个 @ 前缀以免双引号被吞出错,于是实现了 for /f 式的字符串循环,好处是不受 * 与 ? 的干扰,但是格式要求太严格,远不如以换行符为分隔符进行字符串循环来得方便有效,所以也是个没啥用处的技巧...
复制代码

TOP

这个帖子必须收藏,都是高手的讨论啊。

TOP

  1. ->ver
  2. Microsoft Windows XP [版本 5.1.2600]
  3. ->type aaabc.bat
  4. @echo off
  5. set str="bbs.bathome.net" "t e s t ?" "abc*"
  6. for /f "delims=" %%a in (nul %str%) do echo 第一节为:%%a
  7. ->aaabc.bat
  8. 第一节为:bbs.bathome.net
  9. 系统找不到文件 t e s t ?"。
  10. ->
复制代码

TOP

返回列表