标题: 环境变量延迟扩展的五种常用方法
[打印本页]
作者: q8249014
时间: 2010-1-10 15:31
标题: 环境变量延迟扩展的五种常用方法
@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 ]
作者: bat-zw
时间: 2010-1-11 16:23
精品,学习了。
作者: bat-zw
时间: 2010-1-11 16:55
我再跟一个:
:: 利用set延时
@echo off
(
set "#a=cn-dos"&set #
for /f "tokens=2 delims==" %%a in ('set #') do echo %%a
)
pause>nul
作者: zqz0012005
时间: 2010-1-12 00:05
管道机制
http://bbs.verybat.org/verybat.o ... mp;page=1#pid155537
for /f 机制
http://www.bathome.net/viewthrea ... amp;page=1#pid42252
作者: q8249014
时间: 2010-1-20 21:46
回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的加分与肯定
作者: deepimpact
时间: 2010-4-4 16:29
q8249014兄对管道很有研究,有个问题想请教一下
set/p<nul=1|findstr .
会输出"1\n"
set/p<nul="1"|findstr .
却没有输出
不知是何原因
作者: zaixinxiangnian
时间: 2010-4-4 20:02
zqz0012005 q8249014 你们提到这些都非常经典,不过对于我们这些初学批处理的来说有点难理解
作者: HAT
时间: 2010-4-5 10:39
标题: Re 7 楼
很正常。
人家学了那么多年才有这点功底,你初学就像一下都搞清楚?
大家都不是天才,踏踏实实学,慢慢会理解的。
作者: q8249014
时间: 2010-5-1 18:00
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~
作者: qzwqzw
时间: 2010-5-1 23:42
楼主老兄喜欢举例来说明问题
可惜有些问题仍然没有说透彻
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匹配正常匹配
作者: gudou
时间: 2010-5-2 13:39
都是达人级的……小可拜上学习
作者: q8249014
时间: 2010-5-2 15:32
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应该可以找到答案
作者: qzwqzw
时间: 2010-5-2 16:41
正在写回复
老兄有空的话再多等等
('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 ]
作者: q8249014
时间: 2010-5-2 17:02
占位 还得考虑一下
过些时间在回吧 我觉得是findstr接收问题
已经想到一种原因但无法证明,最近实在没时间,过段时间回复大家在讨论下
[
Last edited by q8249014 on 2010-5-4 at 16:18 ]
作者: basswood
时间: 2010-5-25 11:15
现在已很少用这个了,纯支持一下...