无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 2803|回复: 2
打印 上一主题 下一主题

[讨论]for的tokens细节或者缺陷

[复制链接]
跳转到指定楼层
1#
发表于 2012-3-13 12:27:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
转自CN-DOS论坛

刚才看到讨论for的tokens
虽然有一两篇精贴
但被众多口水淹没
让人很没有讨论的心情
索性换到正式场合另开新帖讨论
----------------------------------------
上文几位老兄讲得大致不错
只是在某些细节上还点得不够到位
tokens中有几个重要的细节也是缺陷
是所有喜欢玩脚本的老兄都应该注意的

为讨论方便首先建立一个测试文件
echo 1 2 3 4 5 6 7 8 9 10 11 12 > test

另外以下所有代码都是在命令行中测试
在bat中测试请自行修改
--------------------------------------------------------------------------------
细节或缺陷之一:order

猜猜下句的执行结果
for /f "tokens=9,8,7" %i in (test) do echo %i-%j-%k
是9-8-7吗?
错!是7-8-9
也就是说令牌是按照它在字符串中的排序值赋给变量的
跟代码中的书写顺序毫不相关
所以不管你怎么设置令牌顺序
它总是按照从小到大的顺序赋值给迭代变量
也就是说你可以随意变换tokens的顺序
而不必担心代码执行结果会有什么变化
只有一个例外(下面会有提到)

这对你是一个好消息吗?
至少对于我是一个坏消息!

因为某些时候我就想获得9-8-7的顺序
但我必须自己通过对变量进行重组才能得到这样的顺序
--------------------------------------------------------------------------------
细节或缺陷之二:*

其意义不言自明
获取剩余的所有令牌到一个变量

注意它必须位于所有令牌的尾部
否则会报错
这就是细节之一提到的唯一的例外
它的顺序不能随意变化

*的另外一个特性就是它隐含一个,
也就是说*可以不像其它令牌那样必须前缀一个逗号分隔符

所以以下两句的执行结果都是一样的
for /f "tokens=1,2*" %i in (test) do echo %i-%j-%k
for /f "tokens=1,2,*" %i in (test) do echo %i-%j-%k

因为在cmd的令牌串中间没有空令牌的存在

--------------------------------------------------------------------------------
细节或缺陷之三:m-n

最初写cmd脚本总以为m-n获取第m到n个令牌到一个变量
后来碰壁了才知道根本不是这么回事

再猜猜这句的结果
for /f "tokens=1-7,8,9" %i in (test) do echo %i-%j-%k

它的结果是是1-2-3,而不是1 2 3 4 5 6 7-8-9
也就是说1-7是指将第1-7个令牌分别取到了7个变量中
其中%i %j %k分别接收到了第1,2,3个令牌
第4-9个令牌因为没有变量接收所以被丢弃了

这个细节可以说我很不欣赏
因为逐个获取多个令牌到多个变量是完全可以通过笨办法来实现的
而完整获取多个令牌到一个变量是没有现成的好办法的
比如我想获取3 4 5 6 7到一个变量中该怎么办?

Q 可以用多个单令牌变量拼凑一个多令牌变量吗?
A 请考虑考虑delims的多样性

Q 可以用不带分割符的令牌符号(如1,2,34567,8,9)吗?
A cmd会执着的将34567认做一个超远的令牌(当然找不到)

Q 可以用*解决多令牌变量问题吗
A 那会多取8 9 10 11 12等令牌到变量

所以之后我见到代码里有tokens=1,2,3,4,5,6,7,8等类似代码
总是忍不住吁叹——
真是身在福中不知福啊!
--------------------------------------------------------------------------------
细节或缺陷之四:shift

在我们遇到一个超长令牌串时
会发现使用完26个字母也无法取到足够的令牌

或者遇到一个不知或不等长度的令牌串时
我们根本无法获知第n个令牌究竟是空还是不空

这是我就十分盼望有一个类似shift的处理令牌串移位的指令
能够让我对这个令牌串得心应手的操作
当然在ms没有满足我的要求之前
只好自立更生艰苦创业了

“没有枪没有炮敌人给我们造——”

这个方法也已经有很多人再用了
for /f "tokens=*" %i in (test) call script.cmd %i
当然script.cmd的脚本名更多时候会被改为一个批处理自身的标签

但如果这中间的delims竟然不是空格、跳格、等号、分号
那就够让人头疼的了!
-----------------------------------------------
for /f 的options有固定的优先顺序
当在tokens的值中出现了*,那么对应的delims值就被忽略了
原来
for /f "tokens=1* delims=*" %a in (abcd*efg*hijk*lmn) do echo %a %b

这样的命令取得的值不是
abcd efg hijk lmn而是abcd efg*hijk*lmn
-------------------------------------------------

for可以取到超过26个变量的值,初步猜想是根据ASCII的大小顺序来取的。
test.txt
  1. a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40
复制代码
上面的文本是用逗号分隔,而且就一行。
  1. for /f "delims=, tokens=1-31" %A in (test.txt) do @echo:第1个变量:%A,第27个变量:%[,第29个变量:%],第31个变量:%_
复制代码
结果是:
第1个变量:a1,第27个变量:a27,第29个变量:a29,第31个变量:a31
而且还发现,tokens的个数只能取到31个,超过31个执行就没结果了。
比如:
  1. for /f "delims=, tokens=1-32" %A in (test.txt) do @echo:第1个变量:%A,第27个变量:%[,第29个变量:%],第31个变量:%_
复制代码
这个执行没有任何结果。
当然,for初始变量的可以指定为非字母的字符。
比如:
  1. for /f "delims=, tokens=1-10" %! in (test.txt) do @echo:%!\%"\%#\%$\%&
复制代码
结果是:
a1\a2\a3\a4\a6
对于特殊字符命名的变量还是无法处理,比如本例中的%%(%的ASCII位于$和&之间)这个就无法取到。
------------------------------------------------------
迭代变量可以取更多ASCII字符这很早就有谈论了
只要选对好的起始点大约可以取将近90个变量

而且只要想到cmd乃至windows内部是完全unicode的
那么双字节字符作变量也应该不算稀奇了
for /f "tokens=1-7" %⒈ in (test) do echo %⒉ %⒊ %⒋

这样能设置的迭代变量就数量上而言足够使用了

---------------------------------------------------------

[ 本帖最后由 2011czmxbb52 于 2012-3-13 14:19 编辑 ]
2#
发表于 2012-3-13 16:07:04 | 只看该作者
楼主解释的很详细的啊支持了啊。
回复

使用道具 举报

3#
发表于 2012-3-23 17:55:45 | 只看该作者
强烈 支持,谢谢分享……
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2024-11-28 06:26

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表