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

[其他] [讨论]批处理中特殊字符的解释机制探索

本帖最后由 applba 于 2011-5-20 03:46 编辑

回避了预处理这个概念。

把解释过程了分成了两个阶段,第一轮和第二轮。

第一轮解释
这一轮的解释是全局性的,是无条件的,是发生语句执行前的。
解释的符号有(% ^ ")。
关于这三个符号的优先级问题(即先解释哪个):
%具有最高优先级,^和"具有并列的优先级。(谢谢QZWQZW的指正)
意思就是说所有的%的处理完毕后才开始处理^和";而对于^和",哪个在前面就先处理哪个

第二轮解释
是局部性的,是由具体的命令或语句进行的,一边运行一遍解释。
解释的符号比较多:
通用特殊符号:括号() & && | || > >> < ,如果开启了变量延迟包括解释!及其范围的^
专用特殊符号:各命令自行识别的特殊符号,差别可能很大。如大多数命令都把空格识别为分隔符,echo把它识别为普通符号。
优先级问题:这些特殊符号哪个在前面就先处理哪个,前面的先处理,后面的后处理。
几个符号作用范围: 管道符号(|) 不能超越 输入输出重定向符号(< > >>) 不能超越 命令分隔符(&,&&,||) 不能超越 括号。

call 触发的对%和^的再次解释
即call命令运行时把^和%重新识别特殊字符,并再次启动解释。
call每嵌套一次,都会把%和^ 重新识别为特殊符号,并启动新一轮的解释。
3

评分人数

本帖最后由 applba 于 2011-5-20 03:35 编辑

批处理中特殊字符的解释机制探索


其实批处理文件也是通过调用cmd.exe来解释和运行的。
cmd.exe 在读取批处理中的一个语句(块)后,需要对其中的特殊符号进行功能解释,然后才开始执行它。
这个解释过程应该是分阶段的,分层次的。

1、第一轮 解释

解释的符号有(% ^ ")。
这一轮的解释是全局性的,是无条件的,是发生语句执行前的。

关于这三个符号的优先级问题(即先解释哪个):
%具有最高优先级,^和"具有并列的优先级。(谢谢QZWQZW的指正)
意思就是说所有的%的处理完毕后才开始处理^和";而对于^和",哪个在前面就先处理哪个


(1)CMD.exe在遇到第一个%后,会检查后面紧跟的第一个字符。
        如果后面紧跟的还是一个%,前面一个对后面一个转义,转义后得到一个普通的%字符,前面的%消失。
        如果后面紧跟一个数字,则把%和数字一起解释为一个参数变量变完成值的替换。(变量名以数字开头出错的原因。)
        如果后面紧跟一个其他字符(包含" ^ & |等),则会继续往后寻找配对的%
                如果找到了,%%之间的内容被识别为环境变量名变完成值的替换。(特殊符号在此时已被替换掉!)
                如果没有找到下一个%,此单%会被丢弃。

例子1:
@echo off
set a=1
echo %123%
echo %%a
echo %%a%%
echo %%%a%%%
echo %%%%a%%%%
pause

例子2:
@echo off
echo %%a
echo %%a%%
for /l %%a in (1,1,5) do (
echo %%a
echo %%a%%
)
pause

可以使用使用除%和=以外的特殊字符作为变量名。
因为这些特殊字符在执行前已经以%a%的形式被替换掉。

例子3:
@echo off
set "^^&\=12345"
echo %^^&\%
pause

例子4:
@echo off
set """"=5"
echo %""&"echo fax
echo %"""%&echo fax


(2)cmd.exe在读取到一个引号时,会继续寻找下一个引号。
        如果没有找到,该引号后的同一行中的其他特殊符号(包括^,不包括%)都被转义,同时单引号被保留。
        如果找到了下一个引号,则这两个引号对之间的特殊符号(包括^,不包括%)都被转义,同时引号对被保留。

        例子1:
@echo off
echo "123&echo 456 &
echo 123"&echo 456 &"
echo "%a%"
echo "%%%%"
echo "^^^^
pause

echo "^^^^说明了,"号消去了^的特殊作用(即转义其他字符的作用)。

        例子2:说明一下%的最高优先级
@echo off
SETLOCAL EnablEdElayEdExpansion
set "%abc=5^5
echo !%abc!
echo !abc!
pause

很明显,单独的%被消去了,实际上处理后执行的 set "abc=5^5
       

(3)对^的解释简单很多:^后紧跟的第一个特殊字符都会被转义(包括引号、不包括%)。

例子1:
        @echo off
        echo go^&dir
        echo ^^^^^^^&dir
        echo 123"&echo abc
        echo 123^"&echo abc
        pause>nul
上面的例子中,^转义了",使引号失去了特殊作用(即转义其他字符的作用)。

例子2:        说明一下%的最高优先级
        @echo off
        set "a=&^!"
        echo ^%a%
        pause>nul
在上面例子中,%解释后处理的结果是&^!,^再次把&^!解释成&!。



2、第二轮 解释

这一轮解释是局部性的,是由具体的命令或语句进行的,一边运行一遍解释。

解释的符号比较多:
通用特殊符号:括号() & && | || > >> < ,如果开启了变量延迟包括解释!及其范围的^
专用特殊符号:各命令自行识别的特殊符号,差别可能很大。如大多数命令都把空格识别为分隔符,echo把它识别为普通符号。
优先级问题:这些特殊符号哪个在前面就先处理哪个,前面的先处理,后面的后处理。
几个符号作用范围: 管道符号(|) 不能超越 输入输出重定向符号(< > >>) 不能超越 命令分隔符(&,&&,||) 不能超越 括号。

下面以set命令为例来分析一下专用特殊符号。

set命令在执行时对引号的解释(其实很多命令对引号的处理和这类似)
如果引号出现在行首,看行尾有没有配对的,如果有一起删除,如果没有删除行首的引号。
其他地方的任意数量的引号均被保留。
例子:下面三句效果一样
set "adfdf=dfdfd 89"
set "adfdf=dfdfd 89
set  adfdf=dfdfd 89

set /a 命令对运算符的解释
比如%(第一轮中已经被转义),set /a把它解释为取余运算符。


如果开启了变量变量延迟,这一轮还会处理!以及^。

(1)变量延迟中!的解释机制

在没有开启变量延迟时,!只是作为一个普通字符,在set /a 语句执行时被解释成”逻辑非“。
在开启变量延迟后,!成为了变量引导符,在命令执行时(不是读取时)按照以下规则来解释:
        遇到一个叹号后,如果后面的内容中含有^符号,会被识别为特殊符号并在此启动解释工作!
        遇到第二个叹号后,他们之间的字符串(转义后)被解释成一个变量名并完成变量值的替换。

cmd.exe在读取字符串时是以语句为单位的,而这个语句可能含有多个命令甚至很多行。
而在执行时是以命令为单位的,比如含一个&的组合语句,是分两次执行的。

如此我们来解释一下一个经典的例子:
@echo off
SETLOCAL enabledelayedexpansion
set a=100
set a=ok&echo %a%
set a=ok&echo !a!
pause

上面的set a=ok&echo %a%在读取时就被处理成了set a=ok&echo 100,运行时自然不能得到正确的结果。
set a=ok&echo !a!读取时显然不会被处理,set a=ok 执行完毕后,再执行echo !a!,此时才进行变量值的替换。

!不会导致%被重新识别为特殊符号:
@echo off
SETLOCAL enabledelayedexpansion
echo !%%%%
echo !^^^^
pause

@echo off
SETLOCAL enabledelayedexpansion
set a=100
echo !%%a%%
pause


注意,开启变量延迟后,变量名中不能含有!。


(2)开启变变量延迟后,!后的^符号也会被再次识别为特殊符号,并启动二次解释。

例子1:
@echo off
SETLOCAL enabledelayedexpansion
set "^=1"
Set "^^=11"
Set "^^^^=1111"
echo %^^^^%
echo ^^^^
echo !^^^^
echo !"^^^^^
pause>nul

输出结果:
1111
^^
^
11

对 echo %^^^^% 的分析:
当读取 echo %^^^^% 时,%%之间的所有^被转义仅当作普通符号,%^^^^%将会被替换成变量值1111。
执行时实际上已经变成了 echo 1111

而对于 echo ^^^^ 的分析:
第一个^对第二个^转义,第三个对第四个转义,解释玩后成了^^。
执行时实际上是 echo ^^。

对于 echo !^^^^ 的分析:
读取时,被解释成 echo !^^
执行时由于!的存在,命令再次把^当成特殊符号,并启动转义过程!
最后^^被转义成了^,而!由于至于单个,没有被消去。

对 echo !"^^^^的分析
读取时,引号将^^^^全部被转义成普通字符,引号自身也被保留。
执行时,!触发的二次转义并不理会",最终被处理成 ^^,而!和"都被保留。

看了上面的分析,我们知道了!会使^再次成为特殊字符,不管是否单!还是双!。


小技巧:开启变量延迟后怎么输出单个感叹号?
@echo off
SETLOCAL enabledelayedexpansion
echo ^^!
pause>nul



3.call 触发的对%和^的再次解释

即call命令运行时也把^和%重新识别特殊字符,并再次启动解释。
call每嵌套一次,都会把%和^ 重新识别为特殊符号,并启动新一轮的解释。


(1)call对%的重新解释

@echo off
echo %%%%%%%%%
call echo %%%%%%%%%
call call echo %%%%%%%%%

我解释一下,cmd.exe在读取时会将%%%%%%%%%解释为普通字符%%%%
运行时,call命令又把%识别为特殊字符,将%%%%解释为普通字符%%
如果又嵌套一个call,它在运行时还会这样做,将%%解释为普通字符%
好了,call每运行一次,都会重新把%识别为特殊字符!

这样,我们也可以利用call来延迟变量。
@echo off
set a=ok &call echo %%a%%。
set a=ok &call call echo %%%%a%%%%。
注意了,%的数量会减半这一点!


(2)Call对^的重新解释

echo ^^^^
call echo ^^^^
call call echo ^^^^

@echo off
echo "^^^^
call echo "^^^^
call call echo "^^^^

为什么上面的正常下面的不正常呢?

call或许认为,执行时遇到的单个^肯定是读取时通过^^转义出来的。所以它在执行时想当然的把他们还原,即把一个^替换成两个。
然后又把^识别为特殊符号,又启动对^的解释,2个又变成一个了,然后得到的是正常结果。
如果call不这么做,那个^的命运将和%一样,每call一次都会减少一半。
call嵌套时依然是先还原后解释,嵌套多少次结果都一样!

我们现在使用单个"来转义所有的^,这样批处理在读取时就把^全部当作了普通字符,^没有发生数量减半……
但是call傻傻不知道,只要看到是^,依然对^进行翻倍处理,完全忽略引号的存在……
翻倍后由于引号的依然存在,call看到的依然是普通字符^,并不启动^的解释……
这样导致每call一次,^的数量都会增加一倍……
1

评分人数

TOP

“该图片仅限百度用户交流使用”
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

没txt就得了,图片还是无访问的外链,这发个毛教程,不过的确是有“新意”!

TOP

看了下,挺不错。不过少量说法比较表面化,可能对预处理的理解还不够到位

TOP

教程写的不错
只是不明白图片化的意义
大略是防编辑的

用windows自带的图片和传真查看器看着比较累
大概看了不到三分之一
看到一句话挺有意思
作者还给读者留了一道家庭作业

“在控制台中我们要显示单个%可以使用:echo ^%,想想为什么”
一道好题目
首先可以明确
在控制台中显示单个%直接用echo %就可以
在这里转义字符^实际上是个无关大碍的蛇足
那么为什么在控制台中可以直接显示%呢?

这个题目发扬楼主的风格留给楼下
天的白色影子

TOP

6# qzwqzw

cmd少一层预处理...

TOP

本帖最后由 applba 于 2011-5-16 18:40 编辑

5# zm900612


我起初也想用预处理这个概念来写这边文章,可是到后期发现有些地方有矛盾的,所以我回避了预处理这个概念。大体上,读取时对特殊字符的解释就是“预处理”,运行时对特殊字符的解释就是二次预处理,call运行时还会触发第三次第四次解释工作……这篇文章写的比较简单,也不是很全面,我觉得还是重在建立一个新的知识架构……

TOP

6# qzwqzw


你可以右键该图片——打开方式——IE/chrome/360浏览器都可以打开看的,和浏览网页一样的效果。


现在已经放出了txt了,在二楼

TOP

本帖最后由 applba 于 2011-5-16 18:47 编辑

6# qzwqzw


你不提出我还真没想到,大概是这样的:
在批处理中单独的%(非%i,非%%i,非配对的)在读取时会被丢弃的…而在控制台中,单个的%(非%a%)被识别为普通符号……而^作为转义字符是无法显示的,或者说他转义了一个普通字符,普通字符转义后还是普通字符……

TOP

1# applba
在CMD窗口中,只能一行一行的输入和执行语句,也就是说只能执行单行语句!

cmd 窗口中也可以输入和执行多行多行语句
方法是在命令行中输入()

C:\Documents and Settings\Administrator>(
More? dir
More? cd
More? )
天的白色影子

TOP

本帖最后由 plp626 于 2011-5-16 21:17 编辑

我的观点,大概啊。。。可以帮助新人理解


%既有高级语言反斜杠\的转义特性(仅脚本中对自身转义),又有引号的配对特性,还有遇到 : ~ 0123456789* 的扩展特性,

!则在变量延迟开启时,有引号配对特性,和遇到:的扩展特性,还有二次预处理特性。

^仅是反斜杠转义特性,(除了可以自身转义外,还可对!"以及 &|,()<>=;回车符还有某些特殊控制字符转义)

" 是配对特性,又有对后续字符串的转义特性。

预处理优先级顺序 % 大于 (" ^ &  < > | ) 大于 !
括号内的优先级同。

call会在当前行引发二次预处理。。。

关于括号,。。。 会引出很多问题,,越说越复杂。。

TOP

cmd.exe对批处理中特殊字符的解释机制
上面两步中如果遇到了%,会中断并跳入%的解释,处理完毕后会返回,继续对其他特殊符号进行转义。
applba 发表于 2011-5-16 04:18

我也认为学习其实是一个不断反复打破和重新确定某些旧有概念和思维定势的过程
比如“预处理”的概念在解释特殊字符处理时确实过于笼统
但某些基本的原理应该是经得起考验的
比如特殊字符优先级的问题
%明显比其他特殊字符具有更高的优先级
也就是说cmd的转义字符检查不止一次
cmd第一次读入语句后首先要检查的符号就是%
它在检查完整条语句中所有的%后
才去重新检查语句中其它的字符
所以对于%这里不存在中断与返回的问题
天的白色影子

TOP

10# applba
对于cmd控制台对于%的处理
比你和7楼的理解还要复杂
举几个例子
cmd控制台下for的迭代变量是单个%而不是两个%
cmd控制台下执行echo %%OS% 将显示%OS=Windows_NT
cmd控制台下%var%引用的环境变量若不存在会原样显示而不是扩展为空
天的白色影子

TOP

1# applba
Command.com最初是MS-DOS的命令解释程序,在Windows XP中依然得到了继承,它可以实现cmd.exe的大多数功能。

记得有一篇将Windows中的命令行的帖子
其中就主要讨论了cmd与command的关系
简单来说
Windows NT系列的command.com
是为了兼容MS-DOS时代的16位程序而存在的
是16位dos程序的命令行入口程序
其背后的命令解释器仍然是cmd.exe
只不过中间由ntvdm.exe增加了一道设备虚拟层
天的白色影子

TOP

返回列表