C:\>type c:\output.txt
abcd
C:\>type c:\output.txt | set /p V1=
C:\>set
... A bunch of junk, NOT seeing "V1"
发生什么事了?根据我所见的SET
的所有文档,%V1%应该从上面赋值“abcd”,不是吗?
如果重要的话,我使用的是Windows XP专业版,SP3。
答案 0 :(得分:12)
管道似乎创建了一个新的CMD实例来执行接收管道数据的下一个命令。因此,当管道结束时,该CMD实例退出并且变量丢失。
答案 1 :(得分:3)
我不知道你在<{1}}命令中看到了哪些 ,但set
的输出明确指出:
/ P开关允许您将变量的值设置为用户输入的输入行。
(我的斜体)。我认为set /?
正在从控制台获取输入,无论你想通过标准输入管道输入什么。为什么不等,我不确定。 set /p
也无法设置变量。
但是,如果你想从单行文件中设置一个变量,你可以使用其中一个:
echo xxx | set /p xx=
第二个是最简单的但如果你想获取任意命令的输出它没有多大帮助,但你可能必须先将它指向一个文件。
第一个允许您执行任意命令而不使用临时文件:
for /f "delims=" %%i in (c:\output.txt) do set V1=%%i
set /p V1=<c:\output.txt
this page上有一个有趣的片段,表明它与上下文有关:
好的,我发现了自己的原因。这是因为
for /f "delims=" %%i in ('echo AAA') do set xx=%%i
创建了一个新的上下文,因此变量永远不会出现在当前上下文的其余部分。证明:
|
> set bar=
> echo aaa | (set /p bar= && set bar)
bar=aaa
> set bar
虽然我拒绝评论该结论的真实性。我不知道在这个意义上是什么语境,我只是为了完整性而引起你的注意。
答案 2 :(得分:2)
这不是对问题Why doesn't [..] work?
的原始问题的直接回答,但这个答案对于希望实现这样的逻辑结果的人有用。 :
echo:somevalue|set /p somevar=
正常的解决方法,例如上面的代码,将是:
echo:somevalue>tempfile.txt&set /p somevar=<tempfile.txt
完美地完成了工作,除了它创建了一个必须在之后处理的新文件(除此之外我们需要采取额外的步骤来确保这个新文件不会覆盖现有的文件我们没有创造或从未打算替换。)
虽然确实存在没有办法围绕管道|
运营商的两侧使用文件流来弥合差距,只要涉及环境变量, Windows NT环境和提供脚本所在的卷正在使用NTFS文件系统,可以使用比临时文件更优雅的东西:alternate data streams
让我们直接进入一个示例,说明如何使用它们:
echo:somevalue>".\%~nx0":temp&set /p somevar=<".\%~nx0":temp
此处%~nx0
代表并扩展为我们批处理文件的文件名(基本名称+扩展名),如果它包含空格,则用引号括起来。
注意:强> 虽然可以直接使用
%0
(不带引号)来引用 如果您需要,当前脚本.\%~nx0
更易于管理 在调试脚本时查看命令的扩展值, 特别是如果你的脚本的完整路径很长。
因此,".\%~nx0":temp
指的是名为temp
的自己的脚本的备用数据流(ADS),例如{{1}我们使用myscript.cmd:temp
命令的输出创建(或覆盖)。由于ADS的行为方式与文件的默认流相同,因此echo
和echo
命令没有任何区别。
使用该方法的完整脚本可能如下所示:
set
此处,在脚本的末尾,可以扩展为@echo off
set __self=".\%~nx0"
set __adsname=test.dat
:: Using some input...
set l_input=@FirewallAPI.dll,-23000
echo:Input: %l_input%
echo:
:: ...we process it...
expand_str_res_id.exe %l_input%>%__self%:%__adsname%&set /p l_output=< %__self%:%__adsname%
:: ...and show the output, e.g. "File and Printer Sharing"
echo:Output: %l_output%
echo:
:cleanup
set l_input=
set l_output=
type nul> %__self%:%__adsname%
set __self=
set __adsname=
pause
之类的行type nul> %__self%:%__adsname
用于清除我们刚刚使用的备用数据流的内容。虽然这会将备用数据流的大小设置为零,但它会不擦除它(请参阅下面的注释)。
最后的一些说明:
大多数命令在以源文件提供时无法理解或使用备用数据流。这意味着不能使用以下内容:
type nul> ".\myscript.cmd":test.dat
,例如type
type myscript.cmd:data > myscript_data.txt
,例如copy
copy myscript.cmd:data myscript_data.txt
,例如move
move myscript.cmd:data myscript_data.txt
/ del
,例如erase
del myscript.cmd:data
/ ren
,例如rename
实际上,它实际上只是文件重定向支持备用数据流,但有一个例外(我能想到):ren myscript.cmd:data myscript.cmd:data.txt
命令。
start
编辑/更改文件内容只会影响默认数据流。不过,我们还有一些选择取决于我们想要实现的目标:
:$DATA
保持仅原始文件的默认数据流来创建新文件,之后可以删除原始文件。type myscript.cmd > myscript_clean.cmd
。然后,您可以使用任何程序访问文件的名称(备用)数据流,例如, dir /a-d /r
我希望这有帮助!
答案 3 :(得分:0)
要添加有关如何确认其他答案的简短示例,您可以使用另一个设置命令IE立即检查管道另一侧的变量内容:
ECHO HELLO | (设置/ P hi = &&设置嗨)
将输出
hi = HELLO
但是在执行之后,变量被破坏了((,所以进一步的'set hi'将显示:
未定义环境变量