本文主要以例子展开了讨论:
对每个例子的结果进行分析;并揭示其中的一些现象;
例一:

 
 
  
  1. @echo off  
  2. set "var=kljlk!tsd!21%mk%gd"  
  3. set var 

结果为
 

 
  
  1. var=kljlk!tsd!21gd 


(注意到 此时setlocal默认为disabledelayedexpansion !!不被识别)
因此把%mk%看做环境变量被替换掉;而mk没有被定义因此%mk%被替换为空

再来看加了setlocal enabledelayedexpansion后会是怎样;
例二:

 
  
  1. @echo off&setlocal enabledelayedexpansion  
  2. set "var=kljlk!tsd!21%mk%gd"  
  3. echo.!var! 

结果为

 
  
  1. kljlk21gd 


注意到 此时setlocal声明为enabledelayedexpansion
因此把!tsd!和%mk%均被看做环境变量而被替换掉;而tsd和mk没有被定义因此均被替换为空
(由于优先级的不同;%mk%将在预处理被先被替换,然后!tsd!被替换;原因后面有讲到)


如果把kljlk!tsd!21%mk%gd放到t.txt然后
再通过%%a做为中介进行传递后会有什么现象呢
例三:

 
  
  1. @echo off  
  2. for /f "delims=" %%a in (t.txt) do (   
  3. set "var=%%a"  
  4. set var  

结果为:

 
  
  1. var=kljlk!tsd!21%mk%gd 


如果按照上面所说的此时%mk%应该被替换为空;
但是这里经过一个%%a的中介;此时的结果不会把%mk%替换;即使mk环境变量有定义也不会替换掉;而是按字面输出;

实际上这跟cmd的预处理机制有关;

当CMD读取for语句时,他的所有语句将一同被读取,并完成预处理工作,这其中就包括环境变量%%的扩展;

因为字符串在文本中 并没有以字符串的形式出现在 for语句中;因此其中的%%躲过了预处理机制;

但是当启用延迟环境变量后
例如:
例四

 
 
  
  1. @echo off  
  2. for /f "delims=" %%a in (t.txt) do (   
  3.        set "var=%%a"  
  4.        set var  

结果为:

 
  
  1. var=kljlk21%mk%gd 


首先他会和上面一样对for所有语句一起读取;完成对%%的扩展;(%%的优先级高)
然后当CMD读取了一条完整的语句之后,
它不会立即执行变量的扩展行为,而会在某个单条语句执行之前再进行扩展,也就是说,这个扩展行为被“延迟”了
例如在set "var=%%a"时 只有执行到这条命令前才会对这条命令进行扩展;
而此时%%a已经被kljlk!tsd!21%mk%gd所代替;因此将会对其中的!!进行扩展.
他会识别其中是否存在!;此时的扩展只处理!!而不处理%%(因为对%%的处理过程在先前已经进行完;而那时因为字符串在文本中 并没有以字符串的形式出现在 for语句中;因此其中的%%躲过了预处理机制;);


然而只有当该字符串中含有!时, cmd才会对该字符串进行再次处理;
举例:
例五:

 
 
  
  1. @echo off&setlocal enabledelayedexpansion  
  2. for /f "delims=" %%a in (t.txt) do (  
  3.        set "var=%%a"  
  4.        set var  

t.txt的内容

 
  
  1. kl&>jl^k!tsd!21%mk%gd  
  2. kl&>jlk^!tsd^!21%mk%gd  
  3. dgssdgdg^gds^gdsa 


结果为:

 
  
  1. var=kl&>jlk21%mk%gd  
  2. var=kl&>jlk!tsd!21%mk%gd  
  3. var=dgssdgdg^gds^gdsa 


注意到 第一行的 ^ 已经不存在了;(所在行包含!!)
而第三行的^没有被处理;(所在行不包含!!)

(包含一个!也会对^进行处理;)
由此可见 启用延迟环境变量后 ;
在每次执行语句前;
cmd会检查是否含有!;
如果存在就对其进行必要的预处理后再执行.


注:此时setlocal默认为disabledelayedexpansion因此!tsd!是因为不给识别而不被替换和%mk%不被替换的原因不同.
如果想强迫进行二次转变(即把%mk%的值按其环境变量值警醒替换)再赋值给var可以使用 call set "var=%%a"
也就是说:
例六:

 
 
  
  1. @echo off  
  2. set "mk=152"  
  3. for /f "delims=" %%a in (t.txt) do (   
  4. call set "var=%%a"  
  5. set var  

的结果则为:

 
  
  1. var=kljlk!tsd!21152gd 


call set "var=%%a"
实际上是让命令解释器再来一次预处理来达到目的的


再来看%%a另一个例子
例七:

 
 
  
  1. @echo off  
  2. echo ^&*&^^&%$%^%$#$1213<>:Lksgsd 

结果为:

 
  
  1. &*  
  2. '^' 不是内部或外部命令,也不是可运行的程序  
  3. 或批处理文件。  
  4. 'Lksgsd' 不是内部或外部命令,也不是可运行的程序  
  5. 或批处理文件。 


大家都知道这是因为里面有特殊字符;
但是看下面的例子;把^&*&^^&%$%^%$#$1213<>:Lksgsd放到t.txt中;用%%a做为中介后:
例七:

 
 
  
  1. @echo off  
  2. for /f "delims=" %%a in (t.txt) do (   
  3. echo.%%a  

结果则为:

 
  
  1. ^&*&^^&%$%^%$#$1213<>:Lksgsd 


所有的都是原样输出;丝毫没有因为特殊字符而受影响;(原因上面已讲到)
注:如果t.txt中有直接回车的空行则会跳过;原因在for本身而不是%%a;


再来个!var!的特殊例子
例八:

 
 
  
  1. @echo off  
  2. set "var=^&*&^^&$^$#$1213<>Lksgsd"  
  3. echo.%var% 

结果为:

 
  
  1. 此时不应有 >。 


结果出错

做如下的修改
例九:

 
  
  1. @echo off&setlocal enabledelayedexpansion  
  2. set "var=^&*&^^&$^$#$1213<>Lksgsd"  
  3. echo.!var! 

结果为:

 
  
  1. ^&*&^^&$^$#$1213<>Lksgsd 


结果正确;
但是此种方法仍然受上面所述的原因影响;
比如如果var=后面的值含有!!和%%等则会发生替换;还有比如中间有:则会与set发生关系而导致结果不准确;
当然可以利用%%a中介可以避免 %% 被替换;但是!!却有上述原因没法逃过被替换;



最后给出个综合应用的例子:
对文件指定行进行输出;不论是否含有特殊字符:
这里就把整个文件输出;对指定行可以修改为 findstr /n .* test.txt^|findstr "^10:" 等;

短短的几行代码包含了很多技巧;
 

 
  
  1. @echo off  
  2. for /f "delims=" %%a in ('findstr /n .* test.txt') do (  
  3. set "var=%%a"  
  4. setlocal enabledelayedexpansion  
  5. set var=!var:*:=!  
  6. echo.!var!  
  7. endlocal  


测试文本:
test.txt
 

 
  
  1. "aou"eo  
  2. ;euou%^> 
  3. ::::aeui  
  4.  
  5. :::E2uo alejou 3<o2io|  
  6. ^aue||%ou  
  7.  
  8. aoue eou 2  
  9. euo 8  
  10. ege#6758!7^^9098!98%$&^  
  11. ueyi^^^^aueuo2  
  12. ~ ! @ # $ % ^ & * ( () " ok " No " <>nul  
  13. ege#6758^^^!7^^^^9098^!98%$&^^  
  14.  
  15. ~ ! @ # $ %"   ^   "& * ( () " ok " No " <>nul  
  16. sdsdg:sadgs 

并没有经过大量的测试;诸位帮忙测试;