为什么管道到stdin创建一个无限循环?

Zap*_*Oxx 5 batch-file

我正在试验管道.所以我创建了一个简单的批处理文件(dos/windows),如下所示:

@echo off
echo [+] starting batch file
:start
set /p msg="[+] enter msg: "
echo [+] Your message: %msg%
IF "%msg%"=="x" (
echo [x] end loop
goto exit
) ELSE (
  goto start 
)

:exit
echo [+] bye
Run Code Online (Sandbox Code Playgroud)

只要我从命令行调用它,这个工作正常:

> showAll.bat

S:\80_personalFolder\81_lab\python\ghoul>showAll.bat
[+] starting batch file
[+] enter msg: hello
[+] Your message: hello
[+] enter msg: x
[+] Your message: x
[x] end loop
[+] bye

S:\80_personalFolder\81_lab\python\ghoul>
Run Code Online (Sandbox Code Playgroud)

但是一旦我尝试将输入传递给它,它将在一个无限循环中运行:

> echo hello | showAll.bat
Run Code Online (Sandbox Code Playgroud)

将导致:

[...]
[+] enter msg: [+] Your message: hello
[+] enter msg: [+] Your message: hello
[+] enter msg: [+] Your message: hello
[+] enter msg: [+] Your message: hello
[+] enter msg: [+] Your message: hello
[...]
Run Code Online (Sandbox Code Playgroud)

我不明白这种行为.有人可以解释我监督的内容或我错过的内容!我如何解决这个问题,所以它不会无限期地运行,但会在循环中的下一个提示停止,所以我可以手动退出?

UPDATE!首先感谢我收到的非常好的答案.它帮助我更好地理解发生了什么.所以我修改了代码以在再次调用read之前重置输入var.我想要实现的目标如下:1)一旦脚本从管道输入运行hello并重新进入循环,将管道'echo hello'发送到批处理脚本2)... ...我希望能够手动输入别的东西.相反,它仍将使用脚本将变量设置为的默认值进行无限循环.如何停止管道并切换回默认的标准输入?我想这就是我正在努力解决的真正问题.我甚至不确定这可以做到.

谢谢,最好

MC *_* ND 6

set /p var=命令旨在检索要存储到指示变量中的输入数据.行为是

  • 如果有要读取的数据,则检索数据并将其存储到指示的变量中
  • 如果没有要读取的数据(没有读取数据或所有数据),则set /p失败(没有错误)并且变量不会更改.

第二种情况是您所看到的行为的来源.

  • echo在第一次迭代中读取"hello"(来自命令)
  • 在第二次迭代中,没有任何数据要读取,但变量不会更改.

您的代码(仅检查变量内容)没有看到两种情况之间的差异,因为变量仍保留上一次迭代的数据.

如果没有读取任何数据是循环退出的有效案例,你可以做类似的事情

:start
    set "msg="
    set /p msg="[+] enter msg: "
    if not defined msg goto :exit
Run Code Online (Sandbox Code Playgroud)

如果没有读取数据,则在每次读取和离开之前重置变量.

这也可以用更简单的方式编写

:start
    set /p msg="[+] enter msg: " || goto :exit
Run Code Online (Sandbox Code Playgroud)

这使用条件运算符||(如果前一个命令失败,则执行next命令)如果失败则离开循环set /p.

编辑以适应评论:

所需的行为是从命令中存在的管道读取数据,但是当读取数据时,set /p应该从用户检索数据.首先,我们需要看看管道是如何工作的.

cmd处理管道运营商,创建了两个新的独立进程来处理管道的左侧和右侧.

此时,我们有三个cmd实例(OP问题中的情况):

  • cmd1 :附加到运行typed命令的控制台
  • cmd2 :在管道的左侧运行命令
  • cmd3 :在管道的右侧运行批处理文件

与每个实例关联的输入/输出流(stdin是输入流,stdout是输出流)是:

  • cmd1:stdin是当前的控制台stdin,stdout是当前的控制台stdout
  • cmd2:stdin从中继承控制台cmd1,stdout附加到stdin流中cmd3
  • cmd3:stdin附加到stdoutcmd2,stdout是继承自cmd1

在此方案中cmd3,处理批处理文件无法访问活动的控制台stdincmd1cmd2.

所以,在这一点上,我没有看到实现指示行为的方法.


jeb*_*jeb 2

对于您的示例,当您只接受管道中的一根管线时,很容易修复它。我只添加了“set“msg=x””来将默认值每次重置为“x”,因为set /p当没有新输入时不会更改变量。
并且set /p不在管道上下文中等待(只是非常正确)

@echo off
echo [+] starting batch file
:start
set "msg=x"
set /p msg="[+] enter msg: "
echo [+] Your message: %msg%
IF "%msg%"=="x" (
echo [x] end loop
goto exit
) ELSE (
  goto start 
)

:exit
echo [+] bye
Run Code Online (Sandbox Code Playgroud)