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

[原创] [分享]批处理命令for 另类用法

本帖最后由 CrLf 于 2014-4-29 00:54 编辑

一、十六进制转十进制
  1. ::常见方案
  2. set hex=4F
  3. set /a num=0x%hex%
  4. echo 十六进制数 %hex% 的十进制数为 %num%
复制代码
  1. ::for /l 方案
  2. set hex=4F
  3. for /l %%a in (0x%hex% 1 0x%hex%) do echo 十六进制数 %hex% 的十进制数为 %%a
复制代码
二、useback,你可以安息了
注:useback 不是笔误,它和 usebackq 其实没有区别。
应对字符串本身带有引号的情况:
  1. ::常见方案:
  2. set str="<test>"
  3. for /f useback %%a in ('%str%') do echo %%a
复制代码
  1. ::转义方案
  2. set str="<test>"
  3. for /f %%a in (^"%str%^") do echo %%a
复制代码
处理文件路径必须用引号的情况:
  1. ::常见方案
  2. set "file=%programfiles%\(te)&(st).txt"
  3. for /f "useback delims=" %%a in ("%file%") do echo %%a
复制代码
  1. ::首引号方案(勘误方案,原理见 8 楼,感谢 7 楼的指正与启迪):
  2. set "file=%programfiles%\(te)&(st).txt"
  3. for /f "delims=" %%a in ("%file%
  4. ) do echo %%a
  5. rem 如果%file% 中不含特殊字符,可以写成一行为:
  6. rem     for /f "delims=" %%a in (^"%file%) do echo %%a
复制代码
  1. ::上级路径方案
  2. set "file=%programfiles%\(te)&(st).txt"
  3. for /f "delims=" %%a in ("%file%\test"\..) do echo %%a
  4. rem 时灵时不灵,仅作参考...
复制代码
三、避免 for /f 从命令获取输入时,命令行参数首尾都有引号产生的错误处理字符串字符串本身带引号的情况:
  1. ::错误模拟(假定 %programfiles% 含空格)
  2. for /f %%a in ('"%programfiles%\winrar\rar.exe" v "1.rar"') do echo %%a
  3. rem 错误产生原因见附文
复制代码
  1. ::解决方案
  2. for /f %%a in ('^;"%programfiles%\winrar\rar.exe" v "1.rar"') do echo %%a
复制代码
四、显示 test.txt 中含有“测试”的行
  1. ::find 方案,直接,但是外部命令的效率欠佳
  2. find "测试"<test.txt
复制代码
  1. ::if+变量替换方案,效率和可控性好一些
  2. for /f "delims=" %%a in (test.txt) do (
  3.         set str=%%a
  4.         setlocal enabledelayedexpansion
  5.         if "!str:测试=!" neq "!str!" echo !str!
  6.         endlocal
  7. )
复制代码
  1. ::(感谢 powerbat 指出原纯 for 方案有盲点,现改为 for+if 方案)for+ if 方案,某些情况下是比较好的选择
  2. for /f "delims=" %%a in (test.txt) do (
  3.     for /f "delims=试" %%b in ("[%%a]") do (
  4.         for /f "tokens=2 delims=测" %%c in ("[%%b]") do if %%c==] echo %%a
  5.     )
  6. )
复制代码
五、for 参数的妙用
以往我们认为 delims 的内容无法含双引号(由于 ^ 无法左右 for 内部的引号匹配,所以 "delims="^" 没有效果),以及 eol 不能为空("eol=" 会将双引号设为分隔符),但是换个角度来思考即可迎刃而解:for 参数真的需要双引号吗?
  1. ::以双引号为分隔符
  2. for /f delims^=^" %%a in ("a"b"c") do echo 以 " 为分隔符的第一节为:%%a
复制代码
如此可以避免 for 解析参数时将 " 理解为参数分隔符,之所以要对 = 转义,是因为等号为 for 的默认分隔符,未通过转义符或双引号转义的等号在预处理时会被解释为空格
与 for 参数相关的讨论见:http://bbs.bathome.net/viewthread.php?tid=12500
  1. ::设置 eol 为 null
  2. for /f "delims="eol^= %%a in (";") do echo 设置 eol 为 null 的结果为:%%a
复制代码
经过验证,在以 "delims="eol^= 为参数 for /f 参数时 0x1~0x79 均不是 eol 的值,含 \x0 的行会被自动忽略故无法验证
至于 0x80 之后的扩展字符因为通用性问题用到的机会很少(见注)故未测试,因此可以视为此处 eol 等于 null。
注:eol 存在只取宽字符的首字节的特性,所以“eol=测”("测"的gbk码为B2E2)等效于“eol=悴”("悴"的gbk码为B2E3),故不建议设置 eol 为宽字符
附文:
【三】的例子中无论 rar.exe 存不存在,都能触发错误解析,先看一下 for /? 系统帮助中的这一部分:
    可以用 FOR /F 命令来分析命令的输出。方法是,将
括号之间的 filenameset 变成一个反括字符串。该字符串会
被当作命令行,传递到一个子 CMD.EXE,其输出会被捕获到
内存中,并被当作文件分析
。如以下例子所示:

正如帮助中所述,因为 for /f 从命令获取输入时,实际上是执行了:
  1. cmd /c "%programfiles%\winrar\rar.exe" v "1.rar"
复制代码

而在 cmd /? 的系统帮助中又提到:
如果指定了 /C 或 /K,则会将该开关之后的
命令行的剩余部分作为一个命令行处理,其中,会使用下列逻辑
处理引号(")字符:

   1.  如果符合下列所有条件,则会保留
       命令行上的引号字符:

       - 不带 /S 开关
       - 正好两个引号字符
       - 在两个引号字符之间无任何特殊字符,
         特殊字符指下列字符: &<>()@^|
       - 在两个引号字符之间至少有
         一个空格字符
       - 在两个引号字符之间的字符串是某个
         可执行文件的名称。

   2.  否则,老办法是看第一个字符
       是否是引号字符,如果是,则去掉首字符并
       删除命令行上最后一个引号,保留
       最后一个引号之后的所有文本。
从这里可以看出实例代码中给出的参数在不满足条件1 的同时又满足了条件2,因此预处理时将消除最外层引号各一,所以 cmd.exe 子进程所执行的是 %programfiles%\winrar\rar.exe" v "1.rar,很明显的,这不是我们所希望的,当然破解方法也很简单,只要不让双引号位于参数头或尾,这个隐患即不攻自破,解决方案中的 ^; 即是在传递给子进程的参数前附加一个 ; 符号,子进程认为它是分隔符而忽略,同时也使得条件2 不被满足,同理,也可以使用 ^=、^, 或 @ 来代替 ^;,原理大同小异。
2

评分人数

人不淫荡枉少年
[qq]8379218[/qq]

TOP

多谢楼主,一直不知道,原来16进制可以直接换算10进制的,
这么好的论坛你上哪找,运行测试环境为6.1.7601-64
注:请遵守互联网信息安全,勿用于非法用途

TOP

回复 30# plp626


    这就是 win7 和 xp 的区别...

TOP

  1. ->ver
  2. Microsoft Windows XP [版本 5.1.2600]
  3. ->type aaabc.bat
  4. @echo off
  5. set str="bbs.bathome.net" "t e s t ?" "abc*"
  6. for /f "delims=" %%a in (nul %str%) do echo 第一节为:%%a
  7. ->aaabc.bat
  8. 第一节为:bbs.bathome.net
  9. 系统找不到文件 t e s t ?"。
  10. ->
复制代码

TOP

这个帖子必须收藏,都是高手的讨论啊。

TOP

本帖最后由 CrLf 于 2012-4-2 00:51 编辑

回复 8# CrLf


zqz 给出了一个帖子的链接:http://www.bathome.net/viewthread.php?tid=3614
点进去一看,觉得很有意思,我这才知道 xp 和 win7 对于 for /f 中输入参数的解析方式是不同的,于是用代码测试看二者差异何在。在此不作长篇大论,概括性地总结一下个人理解:

首先,无论在 xp 还是 win7 下,for /f 的特性都是当参数以设备开头时将触发以类似 shift 的方式对参数挨个解析
  1. set str="bbs.bathome.net" "t e s t ?" "abc*"
  2. for /f "delims=" %%a in (nul %str%) do echo 第一节为:%%a
  3. rem win7 下,顺序解析将终止于用户所指定的最后一个参数或所遇见的第一个字符串参数再或者发生找不到指定文件的错误,所以 win7 下可以用非遍历的方法获取第一节的字符串——虽然比遍历方案方便,但是当然不如以双引号为分隔符来得直接,所以这是个没什么价值的方案
复制代码
  1. set str="bbs.bathome.net" "t e s t ?" "abc*"
  2. for /f "delims=" %%a in (nul %str:" "=" @"%) do echo %%a
  3. rem 而在 xp 下,顺序解析不会因为遇到字符串参数而终止,但是对下一个参数进行解析时,会诡异地吞掉该参数的首字符,所以此处要加个 @ 前缀以免双引号被吞出错,于是实现了 for /f 式的字符串循环,好处是不受 * 与 ? 的干扰,但是格式要求太严格,远不如以换行符为分隔符进行字符串循环来得方便有效,所以也是个没啥用处的技巧...
复制代码

TOP

回复 26# qzwqzw


    大工程啊,建议开个专题来论证,最好能深入浅出讲解一番再附上具体的验证流程,这些溯本追源的命题无论是用于加深本质性的理解还是为日后重写 cmd 提供扎实的理论依据都很有必要

TOP

回复 25# CrLf
这只是对cmd命令行分析过程中的半成品
有些不太确认的结论
我会加注意“可能”、“大致”、“也许”等类似的字样
还有些结论因为需要深入分析和多方面确认
包括set对, / + 等分隔符处理上的不同
所以暂时还没有办法贴出来
天的白色影子

TOP

回复 24# qzwqzw


    感觉似乎有道理,但是不知老兄得出的这些结论有何依据?还是仅仅为个人推测呢?没有冒犯的意思,只是想确保论点的“地基”是稳固可靠站得住脚的。

TOP

回复 19# CrLf
18楼的for /f 的bug代码已经在本地测试过了
发生概率是100%,下面是测试效果

for/r确实是for/f的笔误

11楼代码的问题确实有
是我没有注意到
大概分析了下
可能是这样的原因

首先cmd的命令行长度限制是8192个字符
也就是说命令行缓冲区定义的长度是8192个双字节
排除换行符和空字符有效字符是8190个
而环境变量的长度限制8192字符只体现在变量扩展和显示时
在设置时它的变量长度收到命令行缓冲区长度的限制

其次启用变量延迟后
将不在命令行缓冲区存储环境变量的扩展结果
而采用另外一个词法分析缓冲区
这个缓冲区大概是2*8192个双字节的长度
以便于字符串的切分、合并操作的展开

再次cmd支持命令和参数开关之间
除了空格、TAB等普通分隔符之外
也可以有其它的分隔符比如 : . + / [ ] " 等
与普通的分隔符不同
这些分隔符只用于切分命令和参数
不用于参数之间的切分
是cmd为了兼容旧DOS时代的一些用法而做的hack措施
而且它们在词法切分后不会被剔除
所以它们既是分隔符又是操作符
因此在做了命令与参数切分之后
参数串中仍然保留该字符

第四set命令的参数中
很多常见的命令分隔符都会发生转义
TAB、空格、分号、逗号等成为普通字符
等号除了第一个是操作符之外其它也成为普通字符
因此set后的逗号在分隔了set命令和参数之后
继续作为set的参数之一——用作变量名
天的白色影子

TOP

回复 21# lllsoslll


    17楼的代码在win7中运行正常。

TOP

关注中,学习中

TOP

回复 17# powerbat


    for /f ... %%a in ("...")do ....
不知道其他系统是什么状况,
我的Microsoft Windows XP [版本 5.1.2600]
圆括号里的双引号若隔行有异常产生(“不到文件”),是否产生异常和双引号的内容有关
SOS --- >> lllsoslll@163.com

TOP

回复 18# qzwqzw


    win7 下测试,这段代码显示 16 个 d(一个一行),似乎没有出现问题。
   对 useback 的 bug 向来是只闻其名不见其人(也许是用得少吧),刻意寻找也不知从何下手,看来 for /f 的幽灵很狡猾啊

TOP

返回列表