中国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
状态 离线
『楼 主』:  环境变量延迟扩展的五种常用方法 使用 LLM 解释/回答一下


@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 楼』:   使用 LLM 解释/回答一下

精品,学习了。




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

永远的学习者


积分 3105
发帖 1276
注册 2008-3-8
状态 离线
『第 3 楼』:   使用 LLM 解释/回答一下

我再跟一个:

:: 利用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 楼』:   使用 LLM 解释/回答一下

管道机制
http://bbs.verybat.org/verybat.org/viewthread.php?tid=13905&fromuid=37&page=1#pid155537

for /f 机制
http://www.bathome.net/viewthread.php?tid=6537&page=1#pid42252




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





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 5 楼』:   使用 LLM 解释/回答一下

回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 楼』:   使用 LLM 解释/回答一下

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 楼』:   使用 LLM 解释/回答一下

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


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





积分 9023
发帖 5017
注册 2007-5-31
状态 离线
『第 8 楼』:  Re 7 楼 使用 LLM 解释/回答一下

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




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





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 9 楼』:   使用 LLM 解释/回答一下

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= 请按回车键退出. . .


仔细想一下上面这段代码为什么会出现这种情况? ?

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

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

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

到底对不对请看test.txt

4.你知道 和 的区别吗? 请执行一下试试

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

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

好就点到这里 现在明白多出的一个空格来自何处了吧

6. -------------- no

--------------- ?

--------------- ok

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

提示:问题出在 findstr 上

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

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


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

天的白色影子


积分 2343
发帖 636
注册 2004-3-6
状态 离线
『第 10 楼』:   使用 LLM 解释/回答一下

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

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 楼』:   使用 LLM 解释/回答一下

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


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





积分 175
发帖 45
注册 2007-8-4
状态 离线
『第 12 楼』:   使用 LLM 解释/回答一下

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
银牌会员

天的白色影子


积分 2343
发帖 636
注册 2004-3-6
状态 离线
『第 13 楼』:   使用 LLM 解释/回答一下

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

('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 楼』:   使用 LLM 解释/回答一下

占位 还得考虑一下

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

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

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


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




积分 113
发帖 8
注册 2003-12-2
状态 离线
『第 15 楼』:   使用 LLM 解释/回答一下

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


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

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


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



论坛跳转: