[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
原帖由 wc726842270 于 2010-12-20 09:31 发表
有些不解。LZ可以利用多个GOTO(用IF和FOR配合)达到目的效果,最后再结束P就像8L那样不是也一样达到效果了

嗯,是这样的, 8楼的效果是使用了Exit来退出了.
但是他退出了整个程序, 我的本意并不是退出整个程序, 而是接着计算下一个数.
你可以运行一下6楼的代码就知道了.

TOP

原帖由 zqz0012005 于 2010-12-19 22:32 发表
注意批处理的初衷不是一种程序语言,只是为了方便在DOS中操作而把多个命令写在一个bat文件里进行批量处理、执行,并引入了少许基本的流程控制语句(如if判断文件是否存在或以上一条命令执行结果为条件,for遍历文件) ...


我的初衷也不是解决这么一个问题, 只是我在尝试写这样一个代码的时候发现这么一个问题.
然后我就来这里问一下, 我只是好奇为什么goto不能迅速跳出For循环.

您完全可以说,"哦, 这个是CMD的一个bug".

我觉得用批处理写出这个东西来,比用C来写更有成就感, 因为这个东西用C来写太简单了.

可能是我太执着了, 不好意思~

TOP

为什么不试试在call的语句段里放弃exit,调用goto呢?
  1. @echo off
  2. for /l %%i in (1,1,100000000) do (
  3. echo %%i,%time%
  4. call :end
  5. )
  6. :end
  7. goto ee
  8. goto :eof
  9. :ee
  10. echo %time%
  11. pause
复制代码

[ 本帖最后由 caruko 于 2010-12-20 16:11 编辑 ]

TOP

for语句块是一次性装载到内存中的,因此很可能翻译后的代码是类似汇编中的固定次数循环;因为for并没有提供break语句。

而调用call时会重新装载语句代码,因此可能可以中断循环,在call中再调用goto就达到break的目的了。


或许启用变量延迟,也可以达到效果,楼主可以试一下。

[ 本帖最后由 caruko 于 2010-12-20 16:15 编辑 ]

TOP

回复 20楼 的帖子

谢谢.

不过显然没有break.
会call 100000000次:end

TOP

=.=
不知道你怎么回事...
我这边是瞬间就到pause状态了...

TOP

原帖由 caruko 于 2010-12-20 18:16 发表
=.=
不知道你怎么回事...
我这边是瞬间就到pause状态了...

当然是瞬间pause了....
只是这个pause还在for里面~~敲任意键会继续哦.

TOP

回复 18楼 的帖子

这位朋友之前没有去过非常批处理verybat论坛,像这个for的问题等很多原理理论知识都是不少高手从那里探讨总结出来的,zqz0012005就是其中之一,我给的那个跳不出来的例子就是从他的帖子里看来的。所以是不是Bug、是什么原理他当然知道,所以他才给了那样的建议(这里他再次详述了批处理的本质和用途)。

可惜非常批处理竟然关闭了,很多精华我都没有备份下来,实在可惜!

TOP

回复 24楼 的帖子

我只是打个比方,并不是说这是一个bug,虽然这很可能是一个bug.

还有,我的目的不是解决代码问题, 只是本着学习的态度想知道这是什么问题.

对了, 那个跳不出来的例子很好!

TOP

可能这就是for的一个特点吧~
针对这个问题, 只好放弃for了..
使用goto写了一个~
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. :input
  4. set /p n=Please input a number:
  5. if .%n%==. goto exit
  6. set /p p=%n%=<nul
  7. set i=2
  8. set /a n2=%n%/2
  9. :start
  10. if %i% gtr %n2% goto end
  11. set /a t=%n%%%%i%
  12. if .%t%==.0 (
  13.   set /p "p=%i%"<nul
  14.   set /a n/=%i%
  15.   if not .!n!==.1 set /p "p=*"<nul
  16.   set /a n2=!n!/2
  17. ) else (
  18.   set /a i=%i%+1
  19. )
  20. goto start
  21. :end
  22. set /p "p=%n%"<nul
  23. echo.
  24. set n=
  25. goto input
  26. :exit
复制代码

TOP

只有 for /L 是这样的,所有数都要循环。而直接的 for 和 for /f 可以跳出。以下可以看出:
  1. for /l %%a in (1 1 5) do if %%a==3 goto 1
  2. :1
  3. echo 1
  4. for %%a in (1 2 3 4 5) do if %%a==3 goto 2
  5. :2
  6. echo 2
  7. for /f %%a in ('"echo 1&echo 2&echo 3&echo 4&echo 5"') do (
  8.     if %%a==3 goto 3
  9. )
  10. :3
  11. echo 3
  12. pause
复制代码

g:\我的文档\桌面>for /L %a in (1 1 5) do if %a == 3 goto 1

g:\我的文档\桌面>if 1 == 3 goto 1

g:\我的文档\桌面>if 2 == 3 goto 1

g:\我的文档\桌面>if 3 == 3 goto 1

g:\我的文档\桌面>if 4 == 3 goto 1

g:\我的文档\桌面>if 5 == 3 goto 1

g:\我的文档\桌面>echo 1
1

g:\我的文档\桌面>for %a in (1 2 3 4 5) do if %a == 3 goto 2

g:\我的文档\桌面>if 1 == 3 goto 2

g:\我的文档\桌面>if 2 == 3 goto 2

g:\我的文档\桌面>if 3 == 3 goto 2

g:\我的文档\桌面>echo 2
2

g:\我的文档\桌面>for /F %a in ('"echo 1&echo 2&echo 3&echo 4&echo 5"') do (if %a
== 3 goto 3 )

g:\我的文档\桌面>(if 1 == 3 goto 3 )

g:\我的文档\桌面>(if 2 == 3 goto 3 )

g:\我的文档\桌面>(if 3 == 3 goto 3 )

g:\我的文档\桌面>echo 3
3

g:\我的文档\桌面>pause
请按任意键继续. . .


[ 本帖最后由 tmplinshi 于 2010-12-22 19:36 编辑 ]
1

评分人数

TOP

  1. @echo off
  2. setlocal enabledelayedexpansion
  3. :input
  4. set /p input=Please input a number:
  5. if .%input%==.0 goto exit
  6. set /a n=input
  7. if %n% lss 1 (
  8.   echo Out of range!!
  9.   goto input
  10. )
  11. if %n% gtr 999999999 (
  12.   echo Out of range!!
  13.   goto input
  14. )
  15. set /p p=%n%=<nul
  16. set i=2
  17. call:sqrt %n%
  18. :start
  19. ::echo .%sqrtn%.&pause>nul
  20. if %i% gtr %sqrtn% goto end
  21. set /a t=%n%%%%i%
  22. if .%t%==.0 (
  23.   set /p "p=%i%"<nul
  24.   set /a n/=%i%
  25.   if not .!n!==.1 set /p "p=*"<nul
  26.   call:sqrt !n!
  27. ) else (
  28.   set /a i=%i%+1
  29. )
  30. goto start
  31. :end
  32. set /p "p=%n%"<nul
  33. echo.
  34. set input=
  35. goto input
  36. ::=============runing sqrt=================================
  37. :sqrt
  38. set type=%1
  39. set/a xn=type
  40. set/a times=1
  41. if %type% geq 99 set/a times=2
  42. if %type% geq 9999 set/a times=3
  43. if %type% geq 999999 set/a times=4
  44. if %type% geq 99999999 set/a times=5
  45. for /l %%a in (1,1,4) do (
  46.   set/a yn=!xn!*100
  47.   set/a zn=!yn!/100
  48.   if not !yn! lss 0 (
  49.     if !xn!==!zn! (
  50.     set/a xn=!yn!
  51.     )
  52.   )
  53. )
  54. set/a sn=xn
  55. set sqn=1
  56. for /l %%a in (1,1,20) do (
  57.   set/a sqn=sn/sqn+sqn
  58.   set/a sqn=sqn/2
  59. )
  60. ::echo.
  61. ::echo √%type% ≈ !sqn:~0,%times%!.!sqn:~%times%!
  62. set sqrtn=!sqn:~0,%times%!
  63. goto :eof
  64. ::=============runing sqrt=================================
  65. :exit
复制代码

TOP

我也在做质数因子分解的题目,在for中用了goto语句,数字越大的效率越低,逐条指令分析了良久,终于发现问题出在goto上,试着换成call后便好了。
这真是个奇怪的问题,百度了一下,发现bathome里的这个帖子有这个现象的深刻讨论,goto真是让人感觉莫名其妙。

TOP

本帖最后由 cjiabing 于 2011-4-28 14:10 编辑

以前没注意看到这个问题,确实有点……
不过,我想大大们应该可以解释,这似乎是FOR  /L的预处理吧。
在第一个命令时没见它进行预处理,只对第一个数字进行了排序,它按照原过程进行。而在第二行它就进入对所有数字的 FOR /L 分析了,这种分析似乎不受其它命令的控制,用&无效,|也解释不了。
call可以解释,但call本身有点特殊,况且,每个call都意味着设置一个返回命令,比如goto :eof,否则,cmd就会一直挂着这个call的名字。
一直想写一篇关于中止批处理过程的文章的,可惜没空。
从楼主的代码看,楼主这样用一个for产生一个100000000位的数字,然后只取第一个数字就自动跳出(goto),显然楼主的思路也是缺乏效率的,简直是浪费。
goto 的本意是跳出、跳至,我们通常认为它是有去无回的。
而call的常用来调用、呼叫某程序,呼叫完了它遇到goto :eof时自动返回,如果没有goto :eof似乎它就一直挂在那里,除非遇到其它退出命令。
在for中,试图跳出for循环,再想跳回来,那只能用call。用goto是回不来的。而试图用goto中止进程,需要if等的判断。
在for /l中,即使没有任何多余命令,直接goto也无法阻止该命令进行预处理。在for中,我们通常这样理解,首先从集合中挑选因素处理,然后执行do后面的命令。这个过程反复进行,直到for中的内容被抽取完毕。但在for /l中,这个解释似乎站不住脚,因为for /l更像一次性从集合中抽取元素,然后才逐个去执行do后面的命令。试验如下:
  1. @echo off
  2. for /L %%i in (2,2,103333333333333) do goto end
  3. echo.
  4. echo   game over
  5. echo.
  6. pause
  7. exit
  8. :end
  9. echo.
  10. echo   SORRY! STOP!
  11. echo.
  12. pause
复制代码
然后将goto end换成pause,该命令又变回正常的for过程。看来,这个又是具体命令的区别了。cmd常把命令划成三五六等,然后给它们赋予不同的优先权。
在这里看来,只要不跳出for/l的进程,命令是不会出错的,否则for/l死活都要吃完那点草。这是从上面推理下来,唯一合理的解释。
  1. for /L %%i in (2,2,1033333) do (
  2. echo %%i
  3. dir
  4. )
复制代码
再补充一点:
是不是cmd把 for/l和call捆绑到一起了,也就是专门给call预留了空间?
只要for/l跳出进程,for/l就会进入具体的全面的预处理(前面没有进行的),然后设定一个点,以期call出去后能够找到这个回来的点。所以,我们就看到,for /l在跳出前进行了全部处理。
寂寞是黑白的,但黑白不是寂寞,是永恒。BAT 需要的不是可能,而是智慧。

TOP

以前没注意看到这个问题,确实有点……
不过,我想大大们应该可以解释,这似乎是FOR  /L的预处理吧。
在第 ...
cjiabing 发表于 2011-4-28 14:02



    一年多之后再看到这个帖子, 有种说不出的感觉~
谢谢关注!

TOP

返回列表