为什么管道后“设置-P”不起作用?

时间:2010-08-10 07:42:04

标签: batch-file cmd set pipe

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。

4 个答案:

答案 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的行为方式与文件的默认流相同,因此echoecho命令没有任何区别。

使用该方法的完整脚本可能如下所示:

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用于清除我们刚刚使用的备用数据流的内容。虽然这会将备用数据流的大小设置为零,但它会擦除它(请参阅下面的注释)。

最后的一些说明:

  1. 大多数命令在以源文件提供时无法理解或使用备用数据流。这意味着不能使用以下内容:

    • 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命令。

  2. 虽然我们可以很容易地清除/删除备用数据流的内容,但是删除一个非常棘手,因为复制文件或重命名它不涉及任何流(start编辑/更改文件内容只会影响默认数据流。不过,我们还有一些选择取决于我们想要实现的目标:
    • 如果我们只有一个备用数据流,请不要介意一次删除所有,并且文件的内容只是文本,那么我们可以使用以下命令:$DATA保持原始文件的默认数据流来创建新文件,之后可以删除原始文件。
    • 如果我们对卷的管理权限在Windows Vista或更高版本的操作系统下运行,我们可以使用MKLINK创建指向 one <的符号链接/ strong>备用数据流,它将为我们提供与常用文件管理命令一起使用的标准文件名。
    • 或者,可以使用许多可用工具中的一种来操纵备用数据流,例如StreamsLADSAlternateStreamView
  3. 从命令行列出常规文件(包括备用数据流)的有用命令是type myscript.cmd > myscript_clean.cmd。然后,您可以使用任何程序访问文件的名称(备用)数据流,例如, dir /a-d /r
  4. 我希望这有帮助!

答案 3 :(得分:0)

要添加有关如何确认其他答案的简短示例,您可以使用另一个设置命令IE立即检查管道另一侧的变量内容:

ECHO HELLO | (设置/ P hi = &&设置嗨)

将输出

hi = HELLO

但是在执行之后,变量被破坏了((,所以进一步的'set hi'将显示:

未定义环境变量