[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
本帖最后由 Demon 于 2012-8-3 16:57 编辑
  1. for /l %%i in (1 1 1) do (
  2.     echo((1+2)*3=9
  3.     echo(round bracket is best?
  4.     echo(I don't think so.
  5. )
复制代码
http://demon.tw/reverse/cmd-internal-parser.html

http://demon.tw/reverse/cmd-internal-echo.html

简单说几句吧,除了空格" "、等号"="、逗号","、分号";"以及你给出的左括号"("外,其他符号都会对文件进行搜索。

至于你提到的第4点,也没什么离奇的,不要忘了等号"="在一般情况下也是分隔符,
  1. echo+!tmp:\=!
复制代码
CMD在解析(Parse)时会将等号前面的echo+!tmp:\当成命令,而后面的=!当成命令的参数。

显然echo+!tmp:\并不是有效的内部命令,这时CMD会当成外部命令进行文件搜索(效率降低),然而依然无法找到,这时会对命令进行修复(Fix)。

但是如果ENABLEDELAYEDEXPANSION的话,变量延迟拓展是在命令修复之前进行的,CMD会分别对echo+!tmp:\命令和它的参数=!进行变量延迟拓展,

很显然得到的命令是echo+tmp:\,参数是=,修复后的命令是echo,参数是+tmp:\=,最后echo的输出是tmp:\=

结合上面的情况来看,echo并不存在所谓的最佳用法,需要根据具体情况选择,我的建议是一般情况下使用逗号","(出于效率),在需要输出/?开头的字符串时使用加号"+"。

TOP

回复  Demon


    老兄对第 4 点的解释确实很有道理,恍然大悟,实际原因大约就在于此,这和 echo.bat ...
CrLf 发表于 2012-8-3 17:13


更正一下《批处理技术内幕:ECHO命令》的错误,

由于当时没有分析文件搜索的CALL(太复杂懒得跟踪),错误的认为它们的搜索过程都是一样的,在简单分析了一下分析文件搜索的过程之后,发现并不是如此,

虽然+/[]:.\都会调用文件搜索的CALL,如果命令中存在冒号:或者反斜杆\,处理的方法与不存在时是不一样的。

另外,在搜索开始之前斜杆/(即Unix路径分隔符)会被替换成反斜杠\,故斜杆和反斜杆效果是一样的。

具体的处理过程比较复杂,就不展开了,具体到echo而言,echo/ echo: echo\都不会进行实际的文件搜索,只是会调用一些无关痛痒的函数,对效率的影响基本是可以忽略的。

而echo+ echo[ echo] echo.会对工作目录与%PATH%中的目录进行搜索,速度自然会比较慢。

但是用你给出的测试代码测试出来的结果似乎并不符合上面的结论,原因不明,我用的是下面的测试代码:
  1. Set fso = CreateObject("scripting.filesystemobject")
  2. set WshShell = CreateObject("wscript.Shell")
  3. s = "(=,;/\:+[]."
  4. For i = 1 To Len(s)
  5.     c = Mid(s, i, 1)
  6.     h = Hex(Asc(c))
  7.    
  8.     With fso.OpenTextFile(h & ".bat", 2, True)
  9.         .WriteLine "@echo off"
  10.         .WriteLine "set s=%time%"
  11.         For j = 1 To 100
  12.             .WriteLine "echo" & c & ">nul"
  13.         Next
  14.         .WriteLine "set e=%time%"
  15.         .Write "echo echo" & c & " %s% %e%>" & h & ".txt"
  16.     End With
  17.    
  18.     WshShell.Run h & ".bat", 0, True
  19.    
  20.     With fso.OpenTextFile(h & ".txt")
  21.         a = Split(.ReadLine, " ")
  22.     End With
  23.    
  24.     WScript.Echo a(0), TimeDiff(a(1), a(2))
  25.    
  26.     fso.DeleteFile h & ".bat"
  27.     fso.DeleteFile h & ".txt"
  28. Next
  29. Function TimeDiff(s, e)
  30.     t = DateDiff("s", CDate(Left(s, 8)), CDate(Left(e, 8)))
  31.     t = t * 1000 + (Right(e, 2) - Right(s, 2)) * 10
  32.     TimeDiff = t
  33. End Function
复制代码

TOP

回复  Demon


试了下貌似测试结果浮动很大...建议使用 10000 以上的循环次数
另外建议在测试代码中这 ...
CrLf 发表于 2012-8-5 14:45


我当然知道用for的话可以节省IO的时间,但可惜的是for并不能正确的测试出结果,因为for中的echo根本不会触发文件搜索!

4楼测试代码得到的结果之所以+/[]的效率要高于:.\,是因为如果ENABLEEXTENSIONS的话,echo: echo. echo\会多调用一次GetFileAttributes函数,这点我在《批处理技术内幕:ECHO命令》已经说得很清楚了。

对比一下这两段代码:
  1. @echo off&setlocal enabledelayedexpansion
  2. for %%a in (+ / [ ] : . \) do (
  3.     (set timea=!time!
  4.     for /l %%b in (1 1 10000) do echo%%~a
  5.     call :时差 !timea! !time!)>nul
  6.     echo 运行 10000 次 echo%%~a 的用时为: !时差!
  7. )
  8. pause
  9. :时差
  10. for /f "tokens=1-8 delims=:. " %%a in ("%*") do (
  11.     set /a "时差=(((%%e-%%a)*60+1%%f-1%%b)*60+1%%g-1%%c)*100+1%%h-1%%d"
  12. )
复制代码
  1. @echo off&setlocal enabledelayedexpansion
  2. for %%a in (+ / [ ] : . \) do (
  3.     (set timea=!time!
  4.     for /l %%b in (1 1 10000) do setlocal disableextensions&echo%%a&endlocal
  5.     setlocal enableextensions
  6.     call :时差 !timea! !time!)>nul
  7.     echo 运行 10000 次 echo%%~a 的用时为: !时差!
  8.     endlocal
  9. )
  10. pause
  11. :时差
  12. for /f "tokens=1-8 delims=:. " %%a in ("%*") do (
  13.     set /a "时差=(((%%e-%%a)*60+1%%f-1%%b)*60+1%%g-1%%c)*100+1%%h-1%%d"
  14. )
复制代码
我上面给出的VBS测试代码虽然大部分时间都浪费在了IO上面,但是可以认为IO时间基本相同,echo+ echo[ echo] echo.花的时间明显高于其他的:
  1. echo( 70
  2. echo= 70
  3. echo, 60
  4. echo; 70
  5. echo/ 80
  6. echo\ 90
  7. echo: 80
  8. echo+ 680
  9. echo[ 670
  10. echo] 720
  11. echo. 790
复制代码

TOP

echo最多可以输出8191个字符,而不是8187个。

TOP

xp sp3下测试
确认echo最多只能输出是8186个字符
如果echo行后有回车则最多输出8195个字符
加上echo空格 ...
qzwqzw 发表于 2012-8-7 10:10


Windows 7 Ultimate Service Pack 1 英文原版
  1. Dim fso
  2. Set fso = CreateObject("scripting.filesystemobject")
  3. With fso.OpenTextFile("1.bat", 2, True)
  4.     .WriteLine "@echo off"
  5.     .WriteLine ">1.txt echo " & String(8192 * 2, "#")
  6. End With
  7. Dim WshShell
  8. set WshShell = CreateObject("wscript.Shell")
  9. WshShell.Run "1.bat", 0, True
  10. WScript.Echo Len(fso.OpenTextFile("1.txt").ReadAll)
复制代码

TOP

不同系统之间的差异真讨厌,无论是XP还是Windows 7的CMD都是一次读入8191个字节,当命令超过8191个字节时,Windows 7会多次读取直到命令结束,但在echo输出之前又会截断为8191个字符;而XP则直接报错(纯属猜想,未验证)。

除了WMI和ADSI中还是少用GetObject的好。
1

评分人数

    • CrLf: 应该是如此吧,纠结技术 + 1

TOP

返回列表