[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[讨论]批处理for命令的参数和扩展特性

本帖最后由 plp626 于 2011-6-14 19:25 编辑

引号也可作为for /f delims的分割符,只是由于大家思维定势忽略了:
http://bathome.net/thread-12395-1-2.html 6楼
  1. @echo off
  2. for /f tokens^=1*^ delims^=^" %%a in ("sd"z"vc") do echo %%b
  3. pause
复制代码
我对for的一些理解:
http://bathome.net/viewthread.ph ... romuid=353#pid80129 26楼
可以这样理解for的二次扩展特性:
for ~ cmd内部的一个类似call的子过程。。。
:FOR [/F | /R | /D |/L]  [option] %variable IN (<parameters>) DO ...

这个内部子过程有/l /r /d /f 四个开关,有共同的do in 关键字,一对括号;
有最多31个连续(以unicode编码存储)单字符名的内置变量,内置变量以%作为前缀标识;
(相对call,若不shift则为11个,分别为%0 %1 %2 。。。。 %9还有特别参数%*);
由于脚本中第一个%会被cmd预处理解析作为变量值开始扩展
所以要再用一个%对“%”转义(这点很像反斜杠\对自身的转义)

/f 开关有eol,skip,usebackq,delims,tokens,5个关键字,他们作为/f开关的 “第一个参数传递”

调用这个子过程的时候,参数分割符同call的参数分隔符(空格,制表符,逗号,分号,等号);

比如
  1. for /f "tokens=1-2 delims=:" %%a in ("abc:123") do echo %%a %%b
复制代码
我们把参数分割符空格改为;,=也可以正常解析:
  1. for=;/f=;;"tokens=1-2 delims=:";;%%a==in,,("abc:xy"),,=;do,=echo %%a %%b
复制代码
更新[2011-6-14]
内置变量名(统一以unicode编码存储)以%作为前缀标识,最多支持连续31个单字符;
  1. :: cmd命令行下粘贴如下代码
  2. :: "老" 到 "耣" 的unicode编码(连续地)为 "8001" 到 "8023"
  3. set ss=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
  4. for /f "tokens=1-31" %老 in ("%ss%")do @echo %老 %耂 %考 %耄 %者 %耆 %耇 %耈 %耉 %耊 %耋 %而 %耍 %耎 %耏 %耐 %耑 %耒 %耓 %耔 %耕 %耖 %耗 %耘 %耙 %耚 %耛 %耜 %耝 %耞 %耟 %耠 %耡 %耢 %耣
复制代码
2

评分人数

感觉for很难

TOP

确实没想到for还可以这样写,但是感觉只要了解到就行了,常态下的写法会让代码看上去更舒适。
踏实一些点.不要着急.你想要的时间都会给你.2

TOP

拜读完各楼高论,感觉好酣畅淋漓……
不知道从哪里看到一句:"号与^号都是转义符号,从这个角度来理解这些行为,或许要容易接受些,至于几个""与^连用及其先后顺序导致的不同结果,可不可以理解为^的转义优先级高于"的转义优先级,因此"转义的内容可以接着连接^转义的部份,而^转义的部分则默认所有的内容都必须是^转义的,导致其后的"转义不合法?

TOP

好帖啊,不能沉了

TOP

Linux Shell 的秘密早就被摸透了(或者说本来就没有秘密),Windows CMD 至今还是一片混沌。不能怪Windows不开源,只能再次证明玩Linux的大多数确实都是高手(至少会C语言)。Windows下的高手其实也不少,但可能都不屑于玩cmd(这些东西对他们来说可能真的太简单)。

TOP

再谈谈FOR的静态部分和动态部分。
  1. FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
复制代码

在FOR中,有几个地方是FOR的基本语法,不能改变:
1、FOR /F  静态部分
开头表明FOR命令,/F参数,用以区分其他命令和其他参数。
2、["options"]  动态部分
选项可选,并且部分要求高,要求顺序等,而部分要求不高,可以省略空格。我想,在FOR读到此处时,此处类似于一个FOR命令:
  1. for /f "tokens=1,2* delims=, " %%a in ("tokens=1,2,3 delims=, ") do echo %%a %%b
复制代码

提取选项关键词和数值,然后进行核对和处理。
选项外的双引号只是一种便于识别的辅助符号:
  1. C:\>for /f "tokens=1" %i in ("4 2 3")do @echo %i
  2. 4
  3. C:\>for /f tokens^=1 %i in ("4 2 3")do @echo %i
  4. 4
复制代码

而使用转义符才是根本,可能在使用多个转义符时,顾及麻烦,便舍弃转义符而偏爱双引号。而且,在编程过程中,使用双引号引住全部选项也符号常理。
为什么要进行转义呢,可能涉及到一些分隔符之类的特殊符号的处理。
3、变量%variable 动态部分
变量也是一个可以改变的部分。%%a批处理,%a命令提示符。批处理是一种脚本,%%a最终需要转化%a给cmd.exe处理。
一般输入变量能改变的较少,主要是输入变量前后要使用分隔符,不能与参数和IN连在一起。
4、in  静态部分
5、(file-set)   动态部分
集合可以是命令、路径和字符串。
6、DO  静态部分
7、command [command-parameters]   动态部分

整个下来:
FOR /F 动态选项  动态变量%%a in  (动态集合) do  动态命令
FOR——(附加参数)——IN——(指明集合对象)——do——(指明动作)
静态部分是指不能改变的FOR的结构,它们之间必须使用分隔符(而不仅仅是空格,还包括其他默认的分隔符),否则出错。
动态部分是指可以选择的各个参数、变量、对象、动作等,可以自由改变,它们之间有些不必用分隔符。
  1. for /f  ,%%a, ,,in("你好") do echo %%a
复制代码

看来默认分隔符在FOR当中是一个比较普遍适用的。
来猜测一下FOR的大概过程:
在命令窗口时代,输入%%a是要比输入%a要吃力的,而到了脚本时代,难道编程的大哥不怕麻烦了吗?估计从脚本转入cmd.exe执行时,有一个丢失百分号的问题(脱层)。为何会丢失呢?是否是与DOS执行区别?如果无法执行也不必做区别的,原因需要资料方可了解。
获得代码后,批处理开始获取执行顺序,预处理一定行数的代码,我们暂且定位十行吧,为什么不是全部一个bat都预处理呢?因为当它遇到一个几十M的批处理时(不要怀疑这个大的BAT,做程序要有这个考虑)如果全部预处理完的话,估计要花费相当多的时间。而实际上,我们打开一个比较大的批处理,一般不会出现卡住的情况。也或许它获得一行处理一行,预处理只是对一行内的命令进行处理而已。这个不好解释。
然后到获得具体的命令,比如FOR的,以“FOR”这个词语开头的一行代码。获得这行代码后分析它是独立的一行,还是有&连接的多个命令。但是,按照我的实践理解,FOR命令在遇到“FOR”并一路按照FOR的语法来判断这个FOR是否合法,从FOR到/F到选项到输入变量……,只要中间出现一个问题,批处理马上退出。实际上也是这样,所以,批处理的预处理只是针对类似FOR一类的命令,针对执行到的某一行,而非全部命令。因此,如果批处理真的是只按照FOR命令的语法从左到右进行判断,一旦遇到问题马上停止,那么,在没有判断一条完整的代码的情况下,也就是当FOR命令只执行到IN的位置,因为IN和括号连到一起了,发生错误,这时,批处理怎么知道命令是否正确呢?或者,命令是怎么区分出FOR、/F、tokens=、delims=等等命令、参数、选项、变量、符号等?这时,基本的语法结构就发挥很大的作用了,除此之外,利用分隔符可以很好的处理这些元素之间的距离和关系。分隔符的作用就是用来区分元素之间的,如果没有分隔符,容易致使两个命令或参数混淆。但我们也发现,tokens=1delims=m,这里的两个选项搅合到一起了,FOR还是能够认出他们。这么说,获得关键词这时就发挥作用了。比如“tokens=1-3delims=:”这个是正确的,但是“tokens=d1-3delims=:”“tokens=1-3ddelims=:”“tokens=1=3delims=:”“tokens=1-3,8delims=:”等都是错的。这就证明了,FOR是直接提取这两个选项的。
在处理完参数、输入变量之后,就到集合了,集合的处理存在预处理,它有可能是字符串,也可能是路径、命令之类的,特别是命令,需要预先处理,那么,这里就存在一个暂停,FOR暂停,等待集合中的命令先执行。而在这些命令执行前,FOR会先检查代码的是否符合语法。
至于到了DO后面的组合,这个也和集合差不多,存在一个暂停、预处理的过程。
因此,FOR的执行,首先检查是否合乎语法,第二预处理是否能够成功执行,第三才是真正的回显执行过程。
FOR和CALL、goto loop等差不多,只是他将一个重复的动作放到了FOR之中,使得代码更加简便,效率更高。
寂寞是黑白的,但黑白不是寂寞,是永恒。BAT 需要的不是可能,而是智慧。

TOP

本帖最后由 cjiabing 于 2011-10-8 00:02 编辑

回复 60# batman


    哈哈,当时犯糊涂了,现在看都好笑。
    另外,谈谈对FOR /F 选项的一些经历:
    记得前阵子看过类似这个
  1. for /f tokens^=1*^ delims^=^" %%a in ("sd"z"vc") do echo %%b
复制代码

    当时我也犯糊涂了,怎么FOR的tokens的=号前还要转义符呢?但我很快想到,在cmd中,特别是cmd的嵌套中,注意,这个cmd是指在批处理文件里的命令 cmd /q 里的FOR的等号也是需要转义的。如:
  1. for /f "tokens=1*" %%i in ('cmd /v:on /c "@echo off&for /f "usebackq tokens^=*" %%a in ("%1") do (echo ^!random^! %%a)"^|sort') do echo %%j
复制代码

    在这里,第二个FOR必须用转义符,一般在FOR内部保留处理默认分隔符时,一般都需要特殊处理,如:
  1. C:\>for %a in (=) do echo %a
  2. C:\>
  3. C:\>for /f %a in ("=") do echo %a
  4. C:\>echo =
  5. =
复制代码

    如果把外面的FOR去掉,以及把所有转义符去掉,这样也可以:
  1. cmd /v:on /c "@echo off&for /f "usebackq tokens=*" %%a in ("%1") do (echo !random! %%a)"|sort
复制代码

   
  1. cmd /v:on /c "@echo off&for /f "usebackq tokens^=*" %%a in ("%1") do (echo ^!random^! %%a)"|sort
复制代码

    如果不去掉FOR里面的转义符也可以,但“|sort”这个前面的转义符要去掉。那么这里就有一个问题了,为什么这里用不用转义符都一样?难道转义符失灵了?
    通过以上大家可以对FOR命令里的符号有了更多的认识。
    plp626的代码,
  1. for /f tokens^=1*^ delims^=^" %%a in ("sd"z"vc") do echo %%b
复制代码

    在命令提示符窗口也可以执行,但如果去掉tokens=后(转义空格)与delims=(转义双引号)前的等号的转义符,命令就会提示“此时不应有 1* delims。”
    可见,在这里,必须用转义符,否则命令失效。但你想起一个问题没有,FOR命令语法的参数是要用双引号引住的!
   
  1. FOR /F ["options"] %variable IN (file-set) DO command [command-parameters]
复制代码

    看看,这里["options"] 是要用引号的。而楼主的代码没有使用引号。delims后面的一个双引号被转义成分隔符来使用了。因为在命令中,引号一般都是成对出现的,少一个往往会引起错误,而这里没有,证明被转义了。如果你在tokens^=1*^ delims^=^"外面添加一对双引号,命令就会出现语法错误。而如果你把转义符全部去掉,直接“delims="”,这样是会出错的,因为双引号不能作为转义符。
    就像powerbat 所做的测试,在FOR /F后面的选项中,使用双引号与否、使用的位置、数量等都会影响到命令的执行,我也曾经做过这类的试验。比如这样的“for  /f  "tokens=2" "delims=。" %%a in……结果只有一个运行得了。
    从这些问题上看,这些参数的变化正反应出FOR的运行规律。
寂寞是黑白的,但黑白不是寂寞,是永恒。BAT 需要的不是可能,而是智慧。

TOP

57# cjiabing
兄弟有点丢基础了。。。
***共同提高***

TOP

58# caruko
老兄,可为什么我用“tokens=*”的情况下它仍然不显示呢?
寂寞是黑白的,但黑白不是寂寞,是永恒。BAT 需要的不是可能,而是智慧。

TOP

57# cjiabing


tokens=m-n* 的情况下,n之后的字符串不会被分割,这在 for /?中有明确说明。

如果还要分割,那么会破坏字符串,无法得到预期的要求,特别是长字符串的时候,出现预料外的分割的话,无法还原这个字符串了。

TOP

本帖最后由 cjiabing 于 2011-5-28 02:40 编辑

token与delims的困惑:
在以下处理中,在“tokens=* delims=o”一行命令中,已经定义了小写字母“o”为分隔符,为何后面显示出来的时候,还是显示字母“o”?
即使使用“tokens=1,2,* delims=o”,最后几个字母“o”仍然会出现。
在我们的理解中,被定义为分隔符的字符默认是被忽略不显示的,但以上“会显示的分隔符”让人困惑。
类似的问题很多,在定义分隔符提取指定列时,对此往往很无奈。delims或者tokens会消极怠工,不按规定完成任务。
  1. @echo off
  2. for /f "skip=25 usebackq tokens=* delims=echo" %%a in (%0) do echo %%a
  3. echo,
  4. echo, 注意观察最后一行的字母“o”。
  5. echo,
  6. pause
  7. cls
  8. for /f "skip=25 usebackq tokens=* delims=o" %%a in (%0) do echo %%a
  9. echo,
  10. echo, 注意观察最后一行的字母“o”。
  11. echo,
  12. pause
  13. cls
  14. for /f "skip=25 usebackq delims=o" %%a in (%0) do echo %%a
  15. echo,
  16. echo, 注意观察最后一行的字母“o”。
  17. echo,
  18. pause
  19. cls
  20. for /f "skip=25 usebackq tokens=1,2,* delims=o" %%a in (%0) do echo [%%a] [%%b] [%%c]
  21. echo,
  22. echo, 注意观察最后一行的字母“o”。
  23. echo,
  24. pause
  25. exit
  26. echo.&echo.
  27. echo. &set/p=按任意键返回&cls&
  28. echo.使用说明:
  29. c
  30. ch
  31. sssssssss echo eeeeeeeee
  32. ececechechoechoochochco
  33. eceho&echo&echoddls
  34. i love you,i love bathome.
复制代码
寂寞是黑白的,但黑白不是寂寞,是永恒。BAT 需要的不是可能,而是智慧。

TOP

55# powerbat


是这样:
  1. (
  2. for %%a in (1 2 !x! !x!)do echo %%a&set x=y
  3. for %%a in (1 2 !x! !x!)do echo %%a&set x=y
  4. )
复制代码

TOP

52# plp626


for %%z in (1 1)do for %%a in (1 2 !x! !x!)do echo %%a&set x=y
外层的for要展开语句的,里面的语句执行一次后,变量值改变了,这是在第二次for运行之前,当然有效了。
它和把for写成两行没什么本质区别:
for %%a in (1 2 !x! !x!)do echo %%a&set x=y
for %%a in (1 2 !x! !x!)do echo %%a&set x=y

TOP

47# zm900612


for /f "delims= ""tokens=1-3" %a in ("aa bb cc") do echo %a.%b.%c
这样不会报错,但结果是aa.%b.%c,好像tokens=2未起作用。

for /f "eol=a""delims= ""tokens=1-3" %a in ("aa bb cc") ...
powerbat 发表于 2011-5-27 19:22

我晕了,昨天只是想到call参数的特性,试了一下,没研究这么多。
佩服

TOP

返回列表