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

时间:2018-01-30 15:18:49

标签: 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

只要我从命令行调用它就可以正常工作:

> 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>

但是当我尝试将输入传递给它时,它将以无限循环运行:

> echo hello | showAll.bat

将导致:

[...]
[+] 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
[...]

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

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

谢谢,最好

2 个答案:

答案 0 :(得分:6)

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

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

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

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

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

如果没有读取任何数据是循环退出的有效案例,您可以执行类似

的操作
:start
    set "msg="
    set /p msg="[+] enter msg: "
    if not defined msg goto :exit

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

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

:start
    set /p msg="[+] enter msg: " || goto :exit

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

已修改以适应评论:

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

cmd处理管道操作符时,会创建两个新的单独进程来处理管道的左侧和右侧。

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

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

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

  • cmd1stdin是当前控制台stdinstdout是当前控制台stdout
  • cmd2:从stdin继承控制台cmd1stdout附加到stdin
  • 中的cmd3
  • cmd3stdin已附加到stdout的{​​{1}}流,cmd2继承自stdout

在此方案中,处理批处理文件的cmd1无法访问cmd3stdin中处于活动状态的控制台cmd1

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

答案 1 :(得分:2)

对于您的样品,当您只接受管道中的一条线时,它很容易修复。 我只添加了'设置" 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