中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --

联盟域名:www.cn-dos.net  论坛域名:www.cn-dos.net/forum
DOS,代表着自由开放与发展,我们努力起来,学习FreeDOS和Linux的自由开放与GNU精神,共同创造和发展美好的自由与GNU GPL世界吧!

游客:  注册 | 登录 | 命令行 | 会员 | 搜索 | 上传 | 帮助 »
中国DOS联盟论坛 » DOS批处理 & 脚本技术(批处理室) » 环境变量延迟扩展的五种常用方法
作者:
标题: 环境变量延迟扩展的五种常用方法 上一主题 | 下一主题
q8249014
初级用户





积分 175
发帖 45
注册 2007-8-4
状态 离线
『楼 主』:  环境变量延迟扩展的五种常用方法


@echo off
:: Code q8249014
:: 环境变量延迟扩展的五种常用方法
:: 补充一种cmd /c
(
  set "var=test"
  cmd /c echo %%var%% 调用新的命令解释器进行延迟
  call echo %%var%% call延迟
  <nul set/p=|echo %%var%% 管道延迟
  for /f %%i in ('echo %%var%%') do echo %%i for延迟
  setlocal enabledelayedexpansion
  echo !var! 启用延迟环境变量扩展
)
set/p= 请按回车键继续. . .
[ Last edited by q8249014 on 2010-1-20 at 21:44 ]

   此帖被 +22 点积分          点击查看详情   
评分人:【 bat-zw 分数: +20  时间:2010-1-11 16:22
评分人:【 sl543001 分数: +2  时间:2010-1-21 00:23


2010-1-10 15:31
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
bat-zw
金牌会员

永远的学习者


积分 3105
发帖 1276
注册 2008-3-8
状态 离线
『第 2 楼』:  

精品,学习了。



批处理之家新域名:www.bathome.net
2010-1-11 16:23
查看资料  发送邮件  发短消息 网志  OICQ (841615149)  编辑帖子  回复  引用回复
bat-zw
金牌会员

永远的学习者


积分 3105
发帖 1276
注册 2008-3-8
状态 离线
『第 3 楼』:  

我再跟一个:
:: 利用set延时
@echo off
(
set "#a=cn-dos"&set #
for /f "tokens=2 delims==" %%a in ('set #') do echo %%a
)
pause>nul




批处理之家新域名:www.bathome.net
2010-1-11 16:55
查看资料  发送邮件  发短消息 网志  OICQ (841615149)  编辑帖子  回复  引用回复
zqz0012005
中级用户




积分 297
发帖 135
注册 2006-10-21
状态 离线
『第 4 楼』:  

管道机制
http://bbs.verybat.org/verybat.o ... mp;page=1#pid155537

for /f 机制
http://www.bathome.net/viewthrea ... amp;page=1#pid42252



hh.exe ntcmds.chm::/ntcmds.htm
2010-1-12 00:05
查看资料  发短消息 网志  OICQ (411976538)  编辑帖子  回复  引用回复
q8249014
初级用户





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 5 楼』:  

回4f:
     你所提供的2篇帖子都是非常不错的。“预处理和管道”中关于管道机制的探索确有独到之
     处,但也存在不足,比如“pause|pause”为什么提示“过程试图写入的管道不存在。”,
     而“set/p=t|pause”却正常,这里提及一点,pause的标准输出是 “请按任意键继续. . .
     加上一个回车”,而set/p=t的标准输出为t,那么请测试“<nul pause|pause”,是不是正
     常了,管道的问题就提到这里。因为我准备写一篇关于管道机制的帖子,所以关于管道机制
     的其他问题在以后的帖子中再说吧。

     “批处理脚本高级编程技巧——变量嵌套”中也没有讲到for是如何实现变量延迟扩展的,
     你在2f中也只是简单比喻了下,一些朋友可能还是不知道原理。那么,下面就再说说for。
     首先提及一点,for命令没有单独的命令处理模块,它和预处理共用一个命令分析处理模
     块。在预处理过后,控制权交给for,当for进行一些格式匹配解释后,它会在调用一个新
     的命令解释器将('command')中的命令提交该解释器解释执行,然后将执行结果传递给for
     继续for命令的执行,下面以几个实例演示一下。
@echo off
for /f "delims=" %%i in ('echo.%%cmdcmdline%%') do (echo.%%i)
pause
从上面这段代码中可以看出for新调用的命令解释器为“C:\WINDOWS\system32\cmd.exe /c echo.%cmdcmdline%”

既然调用了新的命令解释器,那么预处理自然必不可少,从而就达到了延迟扩展的目的。
@echo off
for /f %%i in ('dir /b /ad %%windir%%^|find /v " "') do (echo.%%i)
pause
上面这段代码是一段很经典的过滤代码,目的是打印windows目录下的所有目录名但不打印含空格的目录名,试想

一下,如果('command')中的命令不再调用新的解释器解释而是直接执行,那么命令将会变成这个样子:

    dir /b /ad %windir%|find /v " "

出现错误提示:无效的命令行开关 - "v"。

上面所说的 “set/p=t|pause” 只是为一个演示,其实我们还可以构造一个空传递,“<nul set/p=|pause”

同时也更新了顶楼的代码,使之更有趣一些

补充一下管道的原理:          管道1|管道2|管道3

        以管道符为分割符,首先启动最后面一个命令(管道3),然后是倒数第二个(管道2),依此类推,然后

        后一个命令会暂停以接收前一个命令的输出,如果命令输出为空则直接退出。

        即:管道2接收管道1,管道3接收管道2,

最后,感谢2f的加分与肯定

2010-1-20 21:46
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
deepimpact
新手上路





积分 3
发帖 2
注册 2008-12-12
状态 离线
『第 6 楼』:  

q8249014兄对管道很有研究,有个问题想请教一下
set/p<nul=1|findstr .
会输出"1\n"
set/p<nul="1"|findstr .
却没有输出

不知是何原因

2010-4-4 16:29
查看资料  发短消息 网志   编辑帖子  回复  引用回复
zaixinxiangnian
初级用户





积分 151
发帖 106
注册 2009-10-9
来自 河南省
状态 离线
『第 7 楼』:  

zqz0012005    q8249014   你们提到这些都非常经典,不过对于我们这些初学批处理的来说有点难理解

2010-4-4 20:02
查看资料  发送邮件  访问主页  发短消息 网志  OICQ (657614933)  编辑帖子  回复  引用回复
HAT
版主





积分 9023
发帖 5017
注册 2007-5-31
状态 离线
『第 8 楼』:  Re 7 楼

很正常。
人家学了那么多年才有这点功底,你初学就像一下都搞清楚?
大家都不是天才,踏踏实实学,慢慢会理解的。



2010-4-5 10:39
查看资料  发短消息 网志   编辑帖子  回复  引用回复
q8249014
初级用户





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 9 楼』:  

Re 6F

1.在回答之前我们先来看一下 findstr 输出的方式

开始-----运行-----cmd.exe-----findstr .-----a-----\r-----b-----\r------F6-----\r

在看一下 find

开始-----运行-----cmd.exe-----find /v ""-----a-----\r-----b-----\r------F6-----\r

以上的输出有什么不同你们自己比较一下

2.
@echo off
echo test|(pause&findstr .)
set/p= 请按回车键退出. . .
仔细想一下上面这段代码为什么会出现这种情况? [test-----est]?

3.set/p<nul=1|findstr .   ---------------  ok

那么它的标准输出是你所说的 "1\n"吗?我们用下面的代码检验一下

set/p<nul=1|^> test.txt findstr .

到底对不对请看test.txt

4.你知道 [<nul set/p=test] 和 [<nul set/p="test" QQ] 的区别吗? 请执行一下试试

5.下面我们来看一下3中的原因

set/p<nul=1|findstr .  这句代码经过命令解释器第一次解释之后变为

[set/p=1 0<nul] 好就点到这里 现在明白多出的一个空格来自何处了吧

6.[set/p<nul="1"|findstr .]  --------------  no

   [set/p<nul="12"|findstr .]   ---------------  ?

   [set/p<nul="1"|find /v ""]   ---------------  ok

第6点请你们根据1-5条的讲解自行分析一下,关键点我都已经提出了。

提示:问题出在 findstr 上

ps:由于没有官方文档的支持,所以上内容仅供参考。

由于最近上网时间少所以前几天才看到各位的回复,O(∩_∩)O~

2010-5-1 18:00
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
qzwqzw
银牌会员

天的白色影子


积分 2342
发帖 635
注册 2004-3-6
状态 离线
『第 10 楼』:  

楼主老兄喜欢举例来说明问题
可惜有些问题仍然没有说透彻

5楼提到的pause|pause问题
是因为后一个pause首先输出“请按任意键继续. . .”后等待管道输入
前一个pause输出“请按任意键继续. . .”(不包含换行)送入管道
后一个pause将第一个字作为暂停确认
其它的字符则被丢弃
此时后一个pause作为管道的接收方就结束了
而前一个pause在稍后时候接收到用户的输入后
会再输出一个换行符
而这个输出此时找不到管道的接收方
自然会提示“过程试图写入的管道不存在。”

而pause<nul|pause正常
是因为前一个pause不再需要用户输入
而连带换行符一起输出
并通过管道交给后一个pause
此时第一个pause就已经结束
不再对管道进行再次写入


for /f %%i in ('dir /b /ad %%windir%%^|find /v " "') do (echo.%%i)
这个例子实际并不太好
因为也完全可以这样写
for /f %%i in ('dir /b /ad %windir%^|find /v " "') do (echo.%%i)
变量嵌套一般不这样使用
而是这样
set var=value
set varname=var
for /f %%i in ('echo %%%varname%%%') do (echo.%%i)


9楼的第1节是说findstr从标准输入匹配文本不可靠
可能会造成匹配的延迟
由于没有官方文档其规律难以掌握
而find则是正常的

第2节是说pause吞吃了管道中的第一个字符"t"作为暂停继续的确认字符

第3节和第5节是说set/p的输出没有回车换行
因为管道前出现了& < 等特殊符号
所以预处理为处理这些符号而插入的空格没有过滤就被送进管道了

第4节的是说set对双引号对的特殊处理
即参数中有双引号对就只处理其中的内容
而忽略引号对外的其它内容

第6节
set/p<nul=1|findstr .
<nul导致多插入一个空格
所以set的输出为1空格

set/p<nul="1"|findstr .
set只处理引号对中的内容
所以输出为1

set/p<nul="12"|findstr .
set只处理引号对中的12
所以输出为12

set/p<nul="1"|find /v ""
find匹配正常匹配

2010-5-1 23:42
查看资料  发短消息 网志   编辑帖子  回复  引用回复
gudou
初级用户





积分 43
发帖 29
注册 2008-3-31
状态 离线
『第 11 楼』:  

都是达人级的……小可拜上学习

2010-5-2 13:39
查看资料  发短消息 网志   编辑帖子  回复  引用回复
q8249014
初级用户





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 12 楼』:  

RE 10F

5f的回复主要是针对4f的,所以只是点了一下,你对 pause|pause 的理解和我当初所理解的一样

for /f "delims=" %%i in ('echo.%%cmdcmdline%%') do (echo.%%i)

你说上面这个例子并不太好,可能是你没有仔细看5f的内容,以上例子是为了说明

('command')中的代码会进行2次预处理,你可以回看一下5f

9f的第一节是为了说明findstr和find输出时的区别,也是为了后面的 [command|findstr .] 做铺垫

第二节你理解正确

第3节和第5节我们放着后面再说

第四节也对

第六节中我给出的代码你可能都没有测试

  [set/p<nul="1"|findstr .]  --------------  no

  [set/p<nul="12"|findstr .]   ---------------  ?

  [set/p<nul="1"|find /v ""]   ---------------  ok


—————————————
set/p<nul="1"|findstr .
set只处理引号对中的内容           引用qzwqzw
所以输出为1
—————————————

以上引用是没有输出的

好,说到这里你们应该可以明白3、5节的意识了

由于没有官方文档的支持,所以不能说太细,但是10f兄说不够透彻

那么就在说一点吧:[command|findstr .] 通过管道最后一次传送过去的字符大于或者等于2才可以正常输出  为什么?

还是那句话没有官方支持只有自己去理解,仔细想一下9f应该可以找到答案

2010-5-2 15:32
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
qzwqzw
银牌会员

天的白色影子


积分 2342
发帖 635
注册 2004-3-6
状态 离线
『第 13 楼』:  

正在写回复
老兄有空的话再多等等

('command')中的代码会进行2次预处理
这我是明白的
我只是说二次预处理一般不这样用而已
所以我说你的示例与我的第一个例子差别不大
也就是说我们一般用三对百分号的形式%%%varname%%%
而不太常用两对百分号的形式%%var%%

关于find与findstr的区别
大概你没有太明确的结论
“通过管道最后一次传送过去的字符大于或者等于2才可以正常输出”
这只是一种针对findstr的近似情况
但是你9楼提供的示例一并不能佐证这个观点
因为那个例子是需要两个回车而不是两个以上的字符
所以这个问题需要再讨论再明确

关于9楼第6节
我只是说set的输出
而不是findstr的输出
这是我书写的疏漏
且不去管他

说一句题外话
有些人很喜欢findstr
能用findstr时绝不用find
我恰好相反
能用find时绝不用findstr
从DOS一路赶来
更加相信find的可靠性
而findstr已暴露的种种问题令人担忧

这体现一个很普遍的原理
“越复杂的越脆弱”
功能越多出错的可能性就越大
正如人类自诩比生灵之长
真要遇到自然灾害
未必会比蚂蚁老鼠更晚灭绝

[ Last edited by qzwqzw on 2010-5-2 at 17:00 ]

2010-5-2 16:41
查看资料  发短消息 网志   编辑帖子  回复  引用回复
q8249014
初级用户





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 14 楼』:  

占位  还得考虑一下

过些时间在回吧  我觉得是findstr接收问题

已经想到一种原因但无法证明,最近实在没时间,过段时间回复大家在讨论下

[ Last edited by q8249014 on 2010-5-4 at 16:18 ]

2010-5-2 17:02
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复
basswood
初级用户




积分 113
发帖 8
注册 2003-12-2
状态 离线
『第 15 楼』:  

现在已很少用这个了,纯支持一下...

2010-5-25 11:15
查看资料  发送邮件  发短消息 网志   编辑帖子  回复  引用回复

请注意:您目前尚未注册或登录,请您注册登录以使用论坛的各项功能,例如发表和回复帖子等。


可打印版本 | 推荐给朋友 | 订阅主题 | 收藏主题



论坛跳转: