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

运行前,cmd.exe对命令字符串进行一些处理,主要是特殊符号的解释。
还包括分辨出哪些是命令名,哪些是参数,具体依据是各种特殊符号(空格、逗号等分隔符)。

cmd.exe对一个“命令字符串”进行识别时,第一个空格之前的内容被识别为“命令名”。
例证:for^ /l %%i (1,1,5) echo %%i ,这里转义了第一个空格,结果运行提示
'for' 不是内部或外部命令,也不是可运行的程序或批处理文件。
第一个空格之后的剩余字符串,如果有可用的分隔符,会继续进行进行参数的分割。
如果使用了双引号或者^等转义了这些分隔符,第一个空格之后的所有剩余字符串会被当作一个整体参数传递。

for运行时,会再次识别这些转义后的特殊符号(空格,逗号等参数分隔符),并自行对他们进行解释(如参数分割)。
当然了如果上面一部的预处理已经分离出了全部或者部分的参数,下面不会重复进行分离。

for的自行解释过程是按照顺序进行的,首先看第一个空格后有没有 /开关,比如是for /f。
例子 for  /f delims^=^+   %%a in ("123+456")  do echo %%a
接着看下一个空格后是不是option,这个option必须是一个连续的字符串。
如果有空格,option会被分割,这个空格后的内容会被当作下一个参数。
但是for 此时只认为下一个参数只能是%%a,所以运行是提示 “此时不应有 *”。
如果有option中有空格或者其他特殊字符导致option被分割时,需要使用引号或者^来。

我觉得各个option之间的处理是通过关键字来判断的,即空格是是切割参数的,而不是切割option的
处理到option参数时,里面的空格会被丢弃。

处理完option之后,下一个空格后的内容只能是%%i,如果不是提示非法。
下一个空格后就是双括号了,再下一个空格之后就是do关键字了,如果不是提示非法……
下一个空格后就是具体的命令、语句或者语句块了,这个没什么好说的了……

%%i经过运行前的预处理,成为了%i,%i在for运行时被解释,即替换(或扩展)成具体的值。



同样call也能再次识别转义后的特殊符号,包括% ^和空格等。
只是call和for的解释机制有出入。

TOP

本帖最后由 applba 于 2011-5-26 19:40 编辑

19# zqz0012005


虽然我没认真学过C,但是还是看懂了,这个对原理解释的比较透,但是有官方根据吗?还是自己想象出来的呢?或者是逆向的?

TOP

不是说for和if是“关键字”吗?难道是微软内部泄露出来的?
  1. -> type test.bat
  2. @prompt -$G$S
  3. echo a;echo
  4. for %%a in (a;echo) do echo a;echo
  5. echo for %%a in (a;echo) do echo a;echo
  6. rem if a;echo==a;echo echo a;echo
  7. if a==a;echo echo a;echo
  8. echo if a;echo==a;echo echo a;echo
  9. @echo\&pause
  10. -> test.bat
  11. -> echo a;echo
  12. a;echo
  13. -> for %a in (a echo) do echo a;echo
  14. -> echo a;echo
  15. a;echo
  16. -> echo a;echo
  17. a;echo
  18. -> echo for %a in (a;echo) do echo a;echo
  19. for %a in (a;echo) do echo a;echo
  20. -> rem if a;echo==a;echo echo a;echo
  21. -> if a == a echo echo a;echo
  22. echo a;echo
  23. -> echo if a;echo==a;echo echo a;echo
  24. if a;echo==a;echo echo a;echo
  25. 请按任意键继续. . .
复制代码
把分号换成逗号、等号效果一样。

可见预处理时对for、if的“参数”有特殊对待,会先把非空格分隔符统一处替换成空格。而for、if引导的命令中的分号、逗号、等号等不改变。
命令行参考:hh.exe ntcmds.chm::/ntcmds.htm
求助者请拿出诚心,别人才愿意奉献热心!
把查看手册形成条件反射!

TOP

楼上貌似是for的模型?
可惜我不懂编程,只能大概地猜下

TOP

把以前的东西帖出来,先啥都不说,你们自己看,仔细看。(可怜以前没人参与讨论)
  1. -> type for.c
  2. #include <stdio.h>
  3. int main(int argc, char *argv[])
  4. {
  5.     int i;
  6.     for(i=0;i<argc;i++)printf("argv[%d]: `%s`\n", i, argv[i]);
  7.     return 0;
  8. }
  9. -> gcc -o for.exe for.c
  10. -> for.exe /f tokens^=1-3^ delims^=^" %a in ("a"b"c") do echo %a-%b-%c
  11. argv[0]: `for.exe`
  12. argv[1]: `/f`
  13. argv[2]: `tokens=1-3`
  14. argv[3]: `delims= %a in (abc) do echo %a-%b-%c`
  15. -> type for.bat
  16. @for.exe /f tokens^=1-3^ delims^=^" %%a in ("a"b"c") do echo %%a-%%b-%%c
  17. -> for.bat
  18. argv[0]: `for.exe`
  19. argv[1]: `/f`
  20. argv[2]: `tokens=1-3`
  21. argv[3]: `delims= %a in (abc) do echo %a-%b-%c`
  22. -> for.exe tokens^=1-3
  23. argv[0]: `for.exe`
  24. argv[1]: `tokens=1-3`
  25. -> test.bat tokens^=1-3
  26. %0: `test.bat`
  27. %1: `tokens`
  28. %2: `1-3`
复制代码
命令行参考:hh.exe ntcmds.chm::/ntcmds.htm
求助者请拿出诚心,别人才愿意奉献热心!
把查看手册形成条件反射!

TOP

本帖最后由 zm900612 于 2011-5-26 19:55 编辑

引申话题:
既然for是函数,那别的内部命令又是什么呢?
看来也是函数咯?如果这个说法成立,那其他内部命令和for和if命令之间的区别是什么呢?

其实现在想来,完全把for和if当成函数的观点也有一点瑕疵,我举个例子:
  1. call for %%%%a in ("123") do echo %%%%a
  2. call if exist %0 echo 123
  3. pause
复制代码
显示的是“'XX'不是内部或外部命令,也不是可运行的程序或批处理文件。”,而别的命令则不存在此问题,这是不是说明cmd中其实并不直接存在for这个模块呢?

TOP

16# zm900612

for确实是一个特别的命令,有一定的解释功能。

TOP

我在想,如果for是个函数的话,它大概分为几大子函数:无开关、/f、/l、/r、/d
而仔细回想一下,发现只要有无开关的for作为基础,就能降低其他几个开关实现的难度。
最有代表性的是for /l和for /f,举个例子
for /l %%a in (1 2 10) do echo %%a
::在这个(1 2 10)中,若不用变量通配替换,如何区分初始值、步数和终止点呢?若用for %%a in (1 2 10)...来判断呢?区分参数的工作一下轻松了许多

for /f "tokens=1* delims=:" %%a in ("123:345") do echo %%b
::for在这里如何去分别设置tokens=1* delims=:呢?我想,如果把无开关的for当成基石,那又降低了实现门槛:for %%a in (tokens^=1* delims^= do set %%a,值得一提的是,我原以为applda兄的那段代码之所以不需要加空格,是因为cmd在预处理时将参数划分好再加上空格(就像对重定向干的事一样),但是后来实验证明并非如此,对参数的划分是for命令(函数)自发的,仔细想想这也有道理,毕竟在cmd中,"tokens=1* delims=:"是以%2的形式传递给for命令(函数),cmd不会去解释参数中的参数,因为那在除了for以外的环境下都将是好心办坏事,所以for函数就和我们平时编写的函数一样,自己动手丰衣足食

TOP

本帖最后由 zm900612 于 2011-5-26 16:44 编辑

草就一个模型,就事论事,不考虑useback等因素,还有一些不用for的情况下不好处理的东西也一笔带过,比如tokens
  1. @echo off
  2. call :for /f "tokens=1* delims=:" %%a in ("%time%") do echo %%b
  3. pause&exit
  4. :for
  5. if %1==/f goto /f
  6. ...
  7. :/f
  8. set test=%2
  9. if "%test:~2%" neq "" set tokens=1* &set delims=: &shift /2
  10. set start=%2
  11. ...
  12. shift /3
  13. if "%~3" neq "%3" goto var
  14. if "%~3" leq " " goto command
  15. goto file
  16. ...
  17. :var
  18. set tmp=%~3
  19. setlocal enabledelayedexpansion
  20. set %%b=!tmp:*:=!
  21. echo !%%b!
  22. endlocal
  23. exit /b
复制代码

TOP

本帖最后由 plp626 于 2011-5-26 16:36 编辑

12# applba

如果从call参数分隔符的角度去理解for /f 的第一参数(skip,delims那些) 和第二参数列表(括号里面的那些)
便不难理解为什么('命令')在“命令”含有逗号时要转义,因为它被当做参数分隔符解析为空了,

当括号里面有|,&,<,>这些特殊符号时为什么要用^转义这个也不难理解了

试想下call:标签 /f 参数1 内置变量 in (参数列表) do 。。。
当这个标签后面所跟的参数列表中出现|,<>,&这些特殊符号时,它会把这些特殊符号后面的字符串作为命令或其他东西解析了,参数列表被截断,而使得括号不能匹配,do关键字找不到,于是for报错了;
=========================
解决此两问题的办法我们除了用^转义|<>&还有参数分隔符外,也可以用双引号来统一解决:

for /f "选项" %%a in ('命令')do 命令 ---->>  for /f "选项" %%a in ('"命令"')do 命令

TOP

6,11楼的话可以结合来看,for /f 各个参数之间的空格可以省略,但为了程序的可读性最好别省略。

TOP

本帖最后由 applba 于 2011-5-26 10:42 编辑

继续补充,for /f "option" %%a in (set)
如果set中没有使用双引号时(但可能使用了单引号、反引号)出现特殊符号是需要使用^转义符号的。
  1. @ECHO OFF
  2. ECHO.
  3. SETLOCAL ENABLEDELAYEDEXPANSION
  4. :: Use WMIC to retrieve date and time
  5. FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') DO (
  6. IF NOT "%%~F"=="" (
  7. SET /A SortDate = 10000 * %%F + 100 * %%D + %%A
  8. SET /A SortTime = 10000 * %%B + 100 * %%C + %%E
  9. SET SortTime=0000000!SortTime!
  10. SET SortTime=!SortTime:~-6!
  11. )
  12. )
  13. SET Sort
  14. pause
复制代码
上面的例子中转义了逗号和管道符号。

TOP

个人不建议省去for/f各个参数间的空格,代码除了简洁还要讲究可读啊。。。
***共同提高***

TOP

看来for真的是个函数了,当初我就在纠结将for看成语块的时候,in前头的部分该理解成什么呢?现在用函数的观点来看,虽然“函数”是个比较万金油的概念,但确实比“语块”的说法更准确

TOP

原来把 options 的=" 转义,就可以省略"",从而做到使用"做分隔符,又学到一招。

TOP

返回列表