I'm currently experimenting with pipes. So I created a simple batch file (dos/windows) as follows:
@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
This works fine as long as I do call it from the commandline:
> 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>
But as soon as I try to pipe input to it it will run in an indefinite loop:
> echo hello | showAll.bat
will result in:
[...]
[+] 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
[...]
I dont understand that behaviour. Could someone explain what I oversee or what I'm missing! How would I fix that, so it wont run indefinitely but will stop with the next prompt within the loop, so I can exit manually?
UPDATE! First of all thanks a lot for the really good answers I received. It helped me a lot to better understand what happened. So I modified the code to reset the input var before read is called again. What I would like to achieve would be the following: 1) piping 'echo hello' to the batchscript 2) once the script ran hello from the pipe input and reenters the loop... 3) ... I would like to be able to manually input something else . instead it will still loop indefinetely with whatever default value the script will set the variable to. how can I stop the piping and switch back to the default stdin ? I guess thats the real issue I'm struggeling with. I'm not even sure this can be done.
Thanks and Best
The set /p var=
command is intended to retrieve input data to be stored into the indicated variable. The behaviour is
set /p
fails (with no error) and the variable is not changed.This second case is the source of the behaviour you are seeing.
echo
command) is read in the first iterationYour code (only checking the variable contents) does not see the difference between the two cases as the variable still holds the data from the previous iteration.
If not reading any data is a valid case for loop exit, you can do something like
:start
set "msg="
set /p msg="[+] enter msg: "
if not defined msg goto :exit
reseting the variable before each read and leaving if no data has been read.
This can be also written in a simpler way as
:start
set /p msg="[+] enter msg: " || goto :exit
This uses the conditional operator ||
(execute next command if the previous one failed) to leave the loop if the set /p
failed.
edited to adapt to comments:
The desired behaviour is to read data from the pipe present in the command, but when the data has been read, then the set /p
should retrieve data from the user. First we need to see how the pipe works.
When cmd
handles the pipe operator, two new separate processes are created to handle the left and right sides of the pipe.
At this moment, we have three cmd
instances (the case in the OP question):
cmd1
: attached to the console running the typed command cmd2
: running the command in the left side of the pipecmd3
: running the batch file in the right side of the pipeThe input/output streams (stdin
is input stream, stdout
is output stream) associated with each instance are:
cmd1
: stdin
is current console stdin
, stdout
is current console stdout
cmd2
: inherits console stdin
from cmd1
, stdout
is attached to the stdin
stream in cmd3
cmd3
: stdin
is attached to the stdout
stream of cmd2
, stdout
is inherited from cmd1
In this scenario cmd3
that is handling the batch file has no way to reach the console stdin
stream active in cmd1
or cmd2
.
So, at this point, I don't see a way to achieve the indicated behaviour.
For your sample it's easy to fix it when you only accept one line from a pipe.
I added only 'set "msg=x"' to reset the default value each time to 'x',
because set /p
doesn't change the variable when there isn't new input.
And set /p
doesn't wait in a pipe context (only quite correct)
@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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With