[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖
49楼又给了我提示。
为什么只有for、if这两个命令可以引导括号包含的多行语句块?(当然干脆不要任何命令引导,单独使用括号也是语句块)
说明预处理时看到有for、if,对待就不同了,所以for、if肯定是参与了预处理的。
由此看来for、if的确有关键字的特征。

TOP

45# powerbat


括号里的参数是for的入口的参数,它改变后,重新调用这个for接口就可被再次读取;
这也就可以说明为什么
  1. set x=0
  2. for %%a in (1 2 !x! !x!)do echo %%a&set x=y
复制代码
不能改变;而
  1. set x=0
  2. for %%z in (1 1)do for %%a in (1 2 !x! !x!)do echo %%a&set x=y
复制代码
却被改变的原因;
=============================================
对于选项部分的参数(不知道该怎么叫,大家理解就好)我也仅是靠现象猜测(仅是便于理解):

选项部分的参数直接和for接口函数功能相关联,但在预处理的时候就被读取固定下来;
只要是在这个预处理阶段读取的语句,不管这个语句怎么改变选项部分的参数
能改变的仅仅是参数的值,而非这个for接口的函数功能了;

TOP

上面的结论好像也不完全对。
for /f "usebackq"delims^=^ tokens^=1-3eol^=a %a in (`echo aa bb cc`) do echo %a.%b.%c
结果:aa.%b.%c
后面的选项没生效。

for /f "usebackqdelims= "tokens^=1-3eol^=b %a in (`echo aa bb cc`) do echo %a.
%b.%c
aa.bb.cc
这个却OK,都生效了。

难道usebackq有点特殊?

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") do echo %a.%b.%c
这样也没问题,而且eol生效了。

for /f "eol=b""delims= ""tokens=1-3" %a in ("aa bb cc") do echo %a.%b.%c
结果:aa.%b.%c,虽然eol生效了,但后面的不生效。

for /f eol^=a"delims= ""tokens=1-3" %a in ("aa bb cc") do echo %a.%b.%c
出错:此时不应有 "delims= ""tokens=1-3"。

for /f "delims= ""tokens=1-3""eol=a" %a in ("aa bb cc") do echo %a.%b.%c
这样没问题,但eol不生效。

for /f "delims= "tokens^=1-3eol^=a %a in ("aa bb cc") do echo %a.%b.%c
很好,不出错,而且所有选项均生效了。

for /f "usebackq delims= "tokens^=1-3eol^=a %a in ("aa bb cc") do echo %a.%b.%c
“系统找不到文件 aa bb cc。”,但这不是语法错误,而是for已经成功运行了,只不过找不到文件(找不到才正确,本来就没有)

for /f "usebackq""delims= "tokens^=1-3eol^=a %a in ("aa bb cc") do echo %a.%b.%c
也不出错,但除了usebackq外其他选项不生效。

for /f "usebackq"delims^=^ tokens^=1-3eol^=a %a in ("aa bb cc") do echo %a.%b.%c
这个当然没问题啦,都生效。

回到开始:
for /f eol^=a"delims= " %a in ("aa bb cc") do echo %a.%b.%c
出错:此时不应有 "delims= "。

看来跟引号的位置有关系,大致如下:
如果引号出现在所有选项前,则第一对引号中的选项都有效;后面如果还有引号包含选项则其中的选项不生效;如果只有一对引号,引号外面还有选项,则可以生效。
如果在引号出现前已经有选项,则如果把后面的选项用引号包含起来会出语法错误。

TOP

for 是内部命令,无需那种调用外部程序时带的参数。

如果for是外部命令,那么for %%i do (set x=%%i) 就无法实现,因为FOR如果不是内部命令,就无法更改FOR外部的全局变量。

同时,大多数外部命令,没有打开stdin的,都不接受 ( 多行语句文本 ) 。

而打开stdin的比如findstr,也是在findstr运行之后才打开stdin,无法在传递参数的初始阶段就接受括号 () 包裹的多行文本。

TOP

46# plp626

你没理解我的意思:
for /l %a in (start,step,end) do ... //在循环体中再怎么改变start,step,end,已经不影响for的条件了
这里指的是类似这种方式:
set /a start=1, step=1, end=3
for /l %%a in (!start!,!step!,!end!) do ping -n 1 192.168.0.%%a >nul && set /a end+=1
这样是无法改变循环条件的。

既然括号里面的参数可以是变量,当然可以在for之前改变变量值,然后再运行for,也就是你那种情况,但与我说的完全不是同一种类型。

TOP

我原以为,for是以类似%1的形式接受参数的,但是下面这个怪异现象又让我迷茫了:
  1. for /f "delims= "tokens^=2 %%a in ("haha test") do echo %%a
  2. ::正确
  3. for /f tokens^=2"delims= " %%a in ("haha test") do echo %%a
  4. ::出错
复制代码
第一条可以执行,的二条却出错,这就与call的情况不一样了,为什么呢?

TOP

45# powerbat


【选项部分的参数】如何改变一直没方法(对大多数人,但我不敢断言就一直没方法);

【括号里的参数】在循环体内改变,这样的例子 【不胜枚举】;
  1. for %%a in (1 2)do set loop=%%a&for /l %%b in (1 1 !loop!)do echo %%b
  2. Set loop=1 1 2
  3. for %%a in (1 1) do for /L %%b in (!loop!) do Set loop=626 1 627& ECHO %%b
复制代码
你这个C语言代码推测很容易被推翻。

TOP

另一个有力的证据是,for /f 的选项中不支持!!引用的的延迟变量。(以下代码前提当然都开启了)
set n=1&for /f "tokens=!n!" %%a in (循环集) do ... //出错
很可能就是因为for /f 是在预处理时解析参数的,这时由于不展开延迟变量,传递给for的就是!n!形式的字符串,而for内部处理这些选项时,发现传给tokens的值是非数字,所以马上报错。

set "str=1a2@d@e@l@i@m@s^^^!3^!4"
for /f "delims=" %%a in ("!str!") do echo %%a
set delims=a
for /f "tokens=1-8 delims=!delims!" %%a in ("!str!") do echo %%a.%%b.%%c.%%d.%%e.%%f.%%g.%%h

结果如下:
C:\>for /F "delims=" %a in ("!str!") do echo %a

C:\>echo 1a2@d@e@l@i@m@s^!3!4
1a2@d@e@l@i@m@s!34 //不要问第2个感叹号为什么消失了,你懂的。

C:\>set delims=a

C:\>for /F "tokens=1-8 delims=!delims!" %a in ("!str!") do echo %a.%b.%c.%d.%e.%f.%g.%h

C:\>echo 1a2@.@.@.@.@.@.^.3
1a2@.@.@.@.@.@.^.3

可见,for不是以变量的!delims!值作为分隔符,而是这个字符串本身(包括感叹号)。
所以for的确是在预处理时就得到了参数字符串,for内部就记住了将要用什么去分割。后来不管这个分隔字符串是否要进行变量展开,已经影响不到for内部记住的东西了。


这像这个例子:
http://www.bathome.net/viewthread.php?tid=12324&page=3&fromuid=29086#pid79319
猜想CMD把for /L 语句转换为C语言是类似这样的:
for /l %a in (start,step,end) do ...
==>
int i = 0;
int iStart = getenv("start");//假设for /L 的三个值都是变量
int iStep = getenv("step");
int iStop = getenv("end"); //在执行C语言的for之前已经把条件都确定了
for (i = iStart; i < iStop; i += iStep) {...} //在循环体中再怎么改变start,step,end,已经不影响for的条件了

TOP

本帖最后由 powerbat 于 2011-5-27 01:42 编辑

29# zqz0012005

原来命令行还有个POSIX规范?

=============
google了一下,POSIX是Portable Operating System Interface of Unix的缩写,这个标准主要是Unix/Linux下遵循的。
不过也看到维基百科“微软的Windows NT至少部分实现了POSIX兼容”。
cmd下各个命令及系统自带的外部命令 参数风格确实不太统一。如果也遵循POSIX标准,会方便很多。

TOP

for、if应该的确是“关键字”而非一般命令,
预处理时就把参数传给它们了,从而它们也进行并影响预处理,而不是等cmd.exe先预处理完了再调用for。
zqz0012005 发表于 2011-5-26 20:50

同意这个观点。

结合21楼的例子和结论:可见预处理时对for、if的“参数”有特殊对待,会先把非空格分隔符统一处替换成空格。而for、if引导的命令中的分号、逗号、等号等不改变。
如果不是有特殊对待(或者说for、if没有参与预处理),那为什么要把for、if参数部分中的非空格分隔符统一替换成空格?

23楼
例证:for^ /l %%i (1,1,5) echo %%i ,这里转义了第一个空格,结果运行提示
'for' 不是内部或外部命令,也不是可运行的程序或批处理文件。

把for换成其他命令,就不会报错。所以这个反而例证了for的特殊性。if类似。

7楼
我一直认为空格本身就不能通过^转义的;
为什么在for中就是例外?

可能真的就是例外。
对普通命令来说,就算预处理时空格确实被转义了,但普通命令是cmd在预处理才调用它并把参数传给它,传递的还是实际空格,转义已经不存在了。
而对for不一样,参数是预处理就传给它了,这时转义符是存在的,for也参与预处理,告诉cmd甚至是它自己决定怎么对待特殊字符。
比如那个for.exe,一个普通外部命令,cmd就是在预处理后才调用它并传参数,由于参数是空格隔开的,所以for.exe看到的参数是多个。
而for /f tokens^=1^,2^ delims^=^" %%a in ("a"b"c") do echo %%b,由于有转义符,for看到的option部分是只是一个参数[tokens=1,2 delims="]

TOP

本帖最后由 qzwqzw 于 2011-5-26 22:49 编辑

for和if命令都要重建语句块
以插入到echo on的运行时序中

因此
如果说echo on的内容都是预处理得来的
那么可以说for和if参与了cmd的预处理
并把自己的语法分析过程也带到了预处理过程中
天的白色影子

TOP

被你发现了,我故意取名为for.exe,就是为了混淆一下,看看大家会不会仔细分析我帖的那些代码。
我后面也说了,这个for.exe与cmd的for完全没有关系。

通过它对参数的解析来对比,再结合for /f 常规用法是需要引号把参数括起来(可能的目的是整体当成一个参数),主要论点是如果不用引号,而是把分隔符转义,for /f 的各个函数对for来说是不是整体作为一个参数。如果这个观点成立,则进一步得出for、if也要参与预处理的观点。
再帖一下:
bat中 for %%a in (a;echo) do echo a;echo&findstr /v ; C:\boot.ini
预处理后是这样的:(不用说,打开回显即可看到)
for %a in (a echo) do echo a;echo  & findstr /v ; C:\boot.ini
注意,括号中的分号被替换成了空格,do后面命令中的则没变。
命令行参考:hh.exe ntcmds.chm::/ntcmds.htm
求助者请拿出诚心,别人才愿意奉献热心!
把查看手册形成条件反射!

TOP

本帖最后由 qzwqzw 于 2011-5-26 22:28 编辑

同意36的的见解
当然某些措辞需要修改一下
“由于某些原因,在运行前的预处理时 没有能够分割参数”
应该说
cmd的预处理不会进行命令行的词法分析
因为它认为这是各命令自己的事情

另外for中其实最容易出问题的倒是usebackq
不仅会产生与正常理解相异的行为
而且可能还会泄露部分内存的数据
Microsoft Windows XP [版本 5.1.2600]
(C) 版权所有 1985-2001 Microsoft Corp.

C:\Documents and Settings\Administrator>for /f "usebackq"  %f in ('df df') do ec
ho %f

C:\Documents and Settings\Administrator>echo df
df
系统找不到文件 。

C:\Documents and Settings\Administrator>for /f "usebackq"  %f in ('df df') do ec
ho %f

C:\Documents and Settings\Administrator>echo df
df

C:\Documents and Settings\Administrator>
天的白色影子

TOP

19楼的GCC代码中的词法分析
与cmd中各命令的词法分析大有不同
编译出的for.exe不等同于cmd的for命令
放在这里容易混淆读者的理解
与楼上贴出这段代码的立意相违背
所以我才刻意强调不要参考它来理解for命令的行为

转义字符^只有对于cmd的预处理才有意义
之后的内外部命令分析和执行时是看不到的
也就不具有任何转义的意义
天的白色影子

TOP

返回列表