为什么不“set -P”管道后工作?

发布于 2024-09-13 06:47:45 字数 243 浏览 2 评论 0原文

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 Pro、SP3(如果有的话)。

C:\>type c:\output.txt
abcd
C:\>type c:\output.txt | set /p V1=

C:\>set
... A bunch of junk, NOT seeing "V1"

What happened? According to all documentation for SET I've seen, %V1% should have been assigned a value of "abcd" from the above, no?

I'm on Windows XP Pro, SP3 if it matters.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心意如水 2024-09-20 06:47:46

我不知道您在 set 命令中看到了什么,但 set /? 的输出清楚地表明:

/P 开关允许您将变量的值设置为用户输入的一行输入。

(斜体)。我认为 set /p 正在从控制台获取输入,无论您尝试通过标准输入输入什么内容。为什么不等待,我不确定。回声xxx | set /p xx= 也无法设置变量。

但是,如果您想从单行文件设置变量,则可以使用其中之一:

for /f "delims=" %%i in (c:\output.txt) do set V1=%%i
set /p V1=<c:\output.txt

第二个是最简单的,但如果您想获取任意命令的输出,则它没有多大帮助,但您可以我们必须首先将其定向到一个文件。

第一个允许您在没有临时文件的情况下执行任意命令:

for /f "delims=" %%i in ('echo AAA') do set xx=%%i

好吧,我自己找到了原因。这是因为 | 创建了一个新的上下文,因此该变量永远不会进入当前上下文的其余部分。证明:
<代码>>设置栏=
<代码>>回声 aaa | (set /p bar= && 设置 bar)
bar=aaa
<代码>>设置栏
环境变量 bar 未定义

尽管我拒绝评论该结论的准确性。我不知道这个意义上的上下文是什么,我只是为了完整性而提请您注意。

I don't know what doco you've seen for the set command but the output of set /? clearly states:

The /P switch allows you to set the value of a variable to a line of input entered by the user.

(my italics). I think set /p is getting its input from the console regardless of what you're trying to pipe in through standard input. Why it's not waiting, I'm not sure. echo xxx | set /p xx= also fails to set the variable.

But, if you want to set a variable from a single line file, you can just use one of these:

for /f "delims=" %%i in (c:\output.txt) do set V1=%%i
set /p V1=<c:\output.txt

That second one is the simplest but it doesn't help much if you want to grab the output of an arbitrary command but you may well have to direct it to a file first.

The first one allows you to execute arbitrary commands without temporary files:

for /f "delims=" %%i in ('echo AAA') do set xx=%%i

There's an interesting snippet on this page which suggests it has to do with contexts:

Ok, I found out why myself. It's because the | creates a new context so the variable never makes it out to the rest of the current context. Proof:
> set bar=
> echo aaa | (set /p bar= && set bar)
bar=aaa
> set bar
Environment variable bar not defined

although I decline to comment on the veracity of that conclusion. I don't know what contexts are in this sense, I'm just bringing it to your attention for completeness.

怪我鬧 2024-09-20 06:47:46

这不是对原始问题的直接答案,该问题问为什么 [..] 不起作用?,但这个答案对于那些寻找 a̲c̲h̲i̲e̲v̲e̲ 逻辑的人来说可能很有用。类似这样的结果:

echo:somevalue|set /p somevar=

正常的解决方法,例如上面的代码,将是:

echo:somevalue>tempfile.txt&set /p somevar=<tempfile.txt

它完美地完成了工作,除了它创建了一个必须在之后处理的新文件(事实上我们需要采取额外的步骤来确保这个新文件不会覆盖我们未创建或从未打算替换的现有文件)。

虽然确实如此,但就环境变量而言,没有办法使用文件流来桥接管道 | 运算符两侧的间隙,因为在Windows NT 环境,并且假设脚本所在的卷使用 NTFS 文件系统,可以使用比临时文件更优雅的东西:备用数据流

让我们直接跳到一个示例来说明如何使用它们:

echo:somevalue>".\%~nx0":temp&set /p somevar=<".\%~nx0":temp

这里 %~ nx0 代表并扩展为批处理文件的文件名(基本名称+扩展名),如果包含空格,则用引号引起来。

注意:
虽然可以直接使用 %0 (不带引号)来引用
如果您需要,当前脚本 .\%~nx0 更易于管理
在调试脚本时查看命令的扩展值,
特别是如果脚本的完整路径很长。

因此,".\%~nx0":temp 指的是我们自己名为 的脚本的备用数据流 (ADS) temp,例如 myscript.cmd:temp,我们使用 echo 命令的输出创建(或覆盖)。由于 ADS 的行为方式与文件的默认流相同,因此 echoset 命令看不出任何差异。

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

@echo off
set __self=".\%~nx0"
set __adsname=test.dat

:: Using some input...
set [email protected],-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,用于清除我们刚才使用的备用数据流的内容。尽管这将备用数据流的大小设置为零,但它不会删除它(请参阅下面的注释)。

最后一些注意事项:

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

    • 类型,例如类型 myscript.cmd:data > myscript_data.txt
    • 复制,例如复制myscript.cmd:data myscript_data.txt
    • 移动,例如move myscript.cmd:data myscript_data.txt
    • del/擦除,例如del myscript.cmd:data
    • ren/重命名,例如ren myscript.cmd:data myscript.cmd:data.txt
    • 等等

    事实上,它实际上只是支持备用数据流的文件重定向有一个例外(我能想到的):start 命令。

  2. 虽然我们可以很容易地清除/擦除备用数据流的内容,但删除一个相当棘手,因为复制文件或重命名它不涉及任何流(嗯:$ DATA those),编辑/更改文件内容仅影响默认数据流。不过,根据我们想要实现的目标,我们有一些选择:
    • 如果我们只有一个备用数据流不介意一次删除所有它们,并且文件的内容只是文本,那么我们可以使用以下命令type myscript.cmd > 通过保留原始文件的默认数据流来创建新文件myscript_clean.cmd,之后即可删除原文件。
    • 如果我们对卷拥有管理权限并且在 Windows Vista 或更高版本的操作系统下运行,我们可以使用 MKLINK 创建到一个备用数据流的符号链接,然后该数据流将为我们提供与常用文件管理命令一起使用的标准文件名.
    • 或者,可以使用众多可用工具之一来操作备用数据流,例如 LADSAlternateStreamView

  3. 从命令行列出常规文件(包括)的有用命令是dir /ad /r。然后,您可以使用任何程序访问文件的名称(备用)数据流,例如 notepad.exe myscript.cmd:data

我希望这会有所帮助!

This is not a direct answer to the original question, which asked Why doesn't [..] work?, but this answer can be useful to people looking to a̲c̲h̲i̲e̲v̲e̲ what would be the logical result of something like this:

echo:somevalue|set /p somevar=

The normal workaround, for example for the code above, would be:

echo:somevalue>tempfile.txt&set /p somevar=<tempfile.txt

which does the work perfectly, except that it creates a new file which has to be dealt with afterwards (on top of the fact that we need to take extra steps to make sure this new file doesn't overwrite an existing one we didn't create or never intended to replace).

While it is true there is no way around using a file stream to bridge the gap across both sides of the pipe | operator as far as environment variables are concerned, being under a Windows NT environment, and provided the volume the script resides on is using the NTFS filesystem, one can use something far more elegant than temporary files: alternate data streams

Let's jump right into an example illustrating how one might use them:

echo:somevalue>".\%~nx0":temp&set /p somevar=<".\%~nx0":temp

Here %~nx0 stands for, and gets expanded to, the filename (base name + extension) of our batch file, enclosed in quotes in case it contains spaces.

Note:
While one could use %0 directly (without quotes) instead to refer to
the current script, .\%~nx0 is more manageable if you ever need to
look at the expanded value of the command while debugging your script,
especially if the full path of your script is quite long.

As such, ".\%~nx0":temp refers to an alternate data stream (ADS) of our own script named temp, for example myscript.cmd:temp, that we create (or overwrite) with the output of the echo command. Since ADS behave the same way as the default stream of a file, the echo and set commands don't see any difference.

A complete script using the method could look like this:

@echo off
set __self=".\%~nx0"
set __adsname=test.dat

:: Using some input...
set [email protected],-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

Here, at the end of the script, the line type nul> %__self%:%__adsname, which could be expanded to something like type nul> ".\myscript.cmd":test.dat, is used to clear the content of the alternate data stream we just used. Although this sets the size of the alternate data stream to zero, it does not erase it (see notes below).

Some final notes:

  1. Most commands cannot understand or work with alternate data streams when they are provided as source files. This means the following ones cannot be used:

    • type, e.g. type myscript.cmd:data > myscript_data.txt
    • copy, e.g. copy myscript.cmd:data myscript_data.txt
    • move, e.g. move myscript.cmd:data myscript_data.txt
    • del/erase, e.g. del myscript.cmd:data
    • ren/rename, e.g. ren myscript.cmd:data myscript.cmd:data.txt
    • and so on

    In fact, it's really only file redirection that supports alternate data streams with one exception (that I can think of): the start command.

  2. Although we can quite easily clear/erase the content of an alternate data stream, deleting one is quite trickier since copying a file or renaming it does not involve any streams (well :$DATA ones), and editing/changing the content of a file only affects the default data stream. Still, we have a few options depending on what we're trying to achieve:
    • If we only have a single alternate data stream or don't mind deleting all of them at once, and the content of the file is only text, then we can create a new file by keeping only the default data stream of the original file using the following command type myscript.cmd > myscript_clean.cmd, after which the original file can be deleted.
    • If we have administrative rights on the volume and operate under Windows Vista or a later version of the OS, we can use MKLINK to create a symbolic link to one alternate data stream, which will then provide us with a standard filename to be used with the usual file management commands.
    • Alternatively, one can use one of the many tools available to manipulate alternate data streams, like Streams, LADS, or AlternateStreamView.
  3. A useful command to list regular files including alternate data streams from the command line is dir /a-d /r. You can then use any program to access the names (alternate) data stream of a file, e.g. notepad.exe myscript.cmd:data

I hope this helps!

娇纵 2024-09-20 06:47:46

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

ECHO HELLO | (set /P hi= && set hi)

将输出

hi=HELLO

但执行后变量被破坏:(,因此进一步的 'set hi' 将显示:

环境变量 hi 未定义

To add a brief example of how to confirm the other answers, you can immediately check the contents of the variable on the other side of the pipe using another set command IE:

ECHO HELLO | (set /P hi= && set hi)

Will output

hi=HELLO

But after execution the variables are destroyed : (, so a further 'set hi' will reveal:

Environment variable hi not defined

南汐寒笙箫 2024-09-20 06:47:45

管道似乎创建了一个新的 CMD 实例来执行接收管道数据的下一个命令。因此,当管道结束时,该 CMD 实例将退出并且变量将丢失。

The pipe seems to create a new CMD instance to carry out the next command that is receiving the pipe data. So when the pipe has concluded, that CMD instance exits and the variable is lost.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文