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

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

[复制链接]
发表于 2011-5-26 20:50:00 | 显示全部楼层
for、if应该的确是“关键字”而非一般命令,
预处理时就把参数传给它们了,从而它们也进行并影响预处理,而不是等cmd.exe先预处理完了再调用for。
上面提到的把非空格分隔符替换为空格,就是一个例子:
bat中 for %%a in (a;echo) do echo a;echo
预处理后是这样的:
for %a in (a echo) do echo a;echo
发表于 2011-5-26 20:59:07 | 显示全部楼层
关键字又是什么概念?如果能弥补“函数论”的缺陷,我不介意叛变...
发表于 2011-5-26 21:05:27 | 显示全部楼层
所有内部命令都相当于是一个函数好吧
发表于 2011-5-26 21:29:43 | 显示全部楼层
19楼的代码似是而非
又没有处理转义符号
不能作为for命令语法分析的参考

把cmd内部命令理解为函数未尝不可
但不要生硬套用函数模型来强解命令行分析

for和if不是关键字而是命令
官方文档提到的关键字
是for命令中的in、do、eol、skip、delims、tokens、usebackq
以及if命令中的not、exist、defined、errorlevel
诸如此类批处理命令中组成命令语法的固定字词
关键字本身不应该具有任何解析上的含义

关于预处理我倾向于这样定义
在批处理中开启echo on后语句所显示的形态即预处理的结果
这个过程cmd对所有语句是统一处理的
此后cmd对语句的语法分析会针对不同命令而有不同
比如各命令对于命令分隔符的处理牵扯到语法分析而各有差异
因此不应算作cmd预处理的范畴
发表于 2011-5-26 21:38:47 | 显示全部楼层
19楼的代码似是而非
又没有处理转义符号
不能作为for命令语法分析的参考

把cmd内部命令理解为函数未尝不可
但不要生硬套用函数模型来强解命令行分析

for和if不是关键字而是命令
官方文档提到的关键字
是f ...
qzwqzw 发表于 2011-5-26 21:29

似乎一步一步地明朗化了
发表于 2011-5-26 22:00:00 | 显示全部楼层
19楼代码的目的已经说了,只是为了试验对分隔符转义后,可不可以让多个参数被整体当作成一个参数。
验证结果表明,就算转义了,对其他命令来说,仍然是不同的参数,而不是整体作为一个参数。
发表于 2011-5-26 22:07:50 | 显示全部楼层
delims与空格的一个问题
http://www.bathome.net/viewthread.php?tid=3692&page=1#pid23376
好像是for的一个bug:一般来说,多个相同字符会被当作一个字符(它们不是整体作为分隔符,新手往往问到这个问题)。但如果指定连续多个空格,空格将无法作为分隔符。
补充以前讨论的一个要点:如果分隔符集里面含有空格,空格必须放在最后。

  1. @echo off
  2. for /f "delims=aa" %%a in ("1a2") do echo %%a
  3. rem 下面的“宽空格”是两个空格(也可以更多)
  4. for /f "delims=  " %%a in ("1 2  3") do echo %%a
  5. for /f "tokens=1,2 delims=aa  " %%a in ("1 2a3  4") do echo %%a.%%b.
  6. pause
复制代码

评分

参与人数 1技术 +1 收起 理由
plp626 + 1 看来我也是新手,学习之。

查看全部评分

发表于 2011-5-26 22:08:04 | 显示全部楼层
36# zqz0012005


由于某些原因,在运行前的预处理时 没有能够分割参数,这些参数会被作为一个整体传递给具体命令然后由具体命令再次进行参数分割。for和call会这么做的。当然有些命令是不会这么做的,比如echo。
发表于 2011-5-26 22:11:32 | 显示全部楼层
19楼的GCC代码中的词法分析
与cmd中各命令的词法分析大有不同
编译出的for.exe不等同于cmd的for命令
放在这里容易混淆读者的理解
与楼上贴出这段代码的立意相违背
所以我才刻意强调不要参考它来理解for命令的行为

转义字符^只有对于cmd的预处理才有意义
之后的内外部命令分析和执行时是看不到的
也就不具有任何转义的意义
发表于 2011-5-26 22:27:04 | 显示全部楼层
本帖最后由 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>
发表于 2011-5-26 22:34:09 | 显示全部楼层
被你发现了,我故意取名为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后面命令中的则没变。
发表于 2011-5-26 22:47:58 | 显示全部楼层
本帖最后由 qzwqzw 于 2011-5-26 22:49 编辑

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

因此
如果说echo on的内容都是预处理得来的
那么可以说for和if参与了cmd的预处理
并把自己的语法分析过程也带到了预处理过程中
发表于 2011-5-27 01:25:01 | 显示全部楼层
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="]
发表于 2011-5-27 01:34:55 | 显示全部楼层
本帖最后由 powerbat 于 2011-5-27 01:42 编辑

29# zqz0012005

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

=============
google了一下,POSIX是Portable Operating System Interface of Unix的缩写,这个标准主要是Unix/Linux下遵循的。
不过也看到维基百科“微软的Windows NT至少部分实现了POSIX兼容”。
cmd下各个命令及系统自带的外部命令 参数风格确实不太统一。如果也遵循POSIX标准,会方便很多。
发表于 2011-5-27 09:17:18 | 显示全部楼层
另一个有力的证据是,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的条件了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|批处理之家 ( 渝ICP备10000708号 )

GMT+8, 2026-3-17 02:49 , Processed in 0.020274 second(s), 7 queries , File On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表