返回列表 发帖

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

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

引号也可作为for /f delims的分割符,只是由于大家思维定势忽略了:
http://bathome.net/thread-12395-1-2.html 6楼
@echo off
for /f tokens^=1*^ delims^=^" %%a in ("sd"z"vc") do echo %%b
pauseCOPY
我对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的参数分隔符(空格,制表符,逗号,分号,等号);

比如
for /f "tokens=1-2 delims=:" %%a in ("abc:123") do echo %%a %%bCOPY
我们把参数分割符空格改为;,=也可以正常解析:
for=;/f=;;"tokens=1-2 delims=:";;%%a==in,,("abc:xy"),,=;do,=echo %%a %%bCOPY
更新[2011-6-14]
内置变量名(统一以unicode编码存储)以%作为前缀标识,最多支持连续31个单字符;
:: cmd命令行下粘贴如下代码
:: "老""耣" 的unicode编码(连续地)为 "8001""8023"
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
for /f "tokens=1-31" %老 in ("%ss%")do @echo %老 %耂 %考 %耄 %者 %耆 %耇 %耈 %耉 %耊 %耋 %而 %耍 %耎 %耏 %耐 %耑 %耒 %耓 %耔 %耕 %耖 %耗 %耘 %耙 %耚 %耛 %耜 %耝 %耞 %耟 %耠 %耡 %耢 %耣COPY
2

评分人数

白天的时候就看到了,不过一直没理解他转义符号的意思。
就干脆在这等待你们讨论了,每次plp626一出来唇枪舌战,总能收获不少东西。哈

TOP

但是有一点我没明白,for 中,^是怎么对空格进行转义的;

call中^为什么不行,为什么call会对字符的^加倍扩展一次。

TOP

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

个人认为/f开关是for所有开关中最重要的一个,对于新手甚至部分老手要掌握这个/f开关的所有参数及其用法是有一定难度的。。。

关于eol,usebackq,skip,tokens,delims这五个/f开关下的参数,记得cn-dos的9527有一篇贴子讲解得很是透彻,只可惜现在联盟不在了。。。

所幸jm著有批处理for从入门到精通的专门教程http://bbs.bathome.net/thread-2189-1-1.html,教程中对for命令进行了深入浅出的讲解并辅以大量的代码和实例加以论证,建议大家在加入此贴讨论之前先看篇教程。。。

在这里,本人只想提醒广大的新手在书写for /f语句时,参数顺序应为"eol= usbackq skip= tokens= delims=",特别是eol和delims参数最好(先前错误地写为必须)一个在最前一个在最后,至于为什么,大家可以思考一下了。。。
***共同提高***

TOP

以引号作为分隔符确实是批处理研究中的一大突破
进而引发了对参数分隔机制的探讨
能解决很多实用的问题
那个帖子应该加精
至少是高亮
这样才能引起更多人的注意
尺有所短寸有所长,学好批处理没商量;
考虑问题复杂化,解决问题简洁化。

心在天山,身老沧州。

TOP

本帖最后由 applba 于 2011-5-25 21:20 编辑

我在补充一点,for /f的各个option之间的空格可以省略。不知道火星了吗?

::普通符号做分隔符
for /f "skip=1 tokens=3,4 delims=<>"
for /f "skip=1tokens=3,4delims=<>"

::引号做分隔符
for /f tokens^=1-3^ delims^=^"
for /f tokens^=1-3delims^=^"
2

评分人数

TOP

楼上,很好!
for /f eol^=^;tokens^=1-3delims^=+ %a in ("5678+678+78")do echo %a %b %cCOPY
在for之前,我一直认为空格本身就不能通过^转义的;
为什么在for中就是例外?
原来,^对空格转义又是我理解上的“多虑”;
这样基本可以把for的参数分割符和call参数分割统一起来;
=========================
但是这里有一点未明:
既然tokens,deliims这些关键字作为/f开关的第一参数,为什么在引号内空格又不能省略?

TOP

又是思维定势:
原来空格一直都是我们的“多虑”
for /f "eol=;tokens=1-3delims=+" %a in ("5678+678+78")do echo %a %b %cCOPY

TOP

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

TOP

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

TOP

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

TOP

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

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

TOP

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

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

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

草就一个模型,就事论事,不考虑useback等因素,还有一些不用for的情况下不好处理的东西也一笔带过,比如tokens
@echo off
call :for /f "tokens=1* delims=:" %%a in ("%time%") do echo %%b
pause&exit
:for
if %1==/f goto /f
...
:/f
set test=%2
if "%test:~2%" neq "" set tokens=1* &set delims=: &shift /2
set start=%2
...
shift /3
if "%~3" neq "%3" goto var
if "%~3" leq " " goto command
goto file
...
:var
set tmp=%~3
setlocal enabledelayedexpansion
set %%b=!tmp:*:=!
echo !%%b!
endlocal
exit /bCOPY

TOP

返回列表