Why does the pipeline in stdin create an indefinite loop?

I'm experimenting with pipes now. 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 while I call it from the command line:

 > 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 connect to it, it will work in an undefined 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 do not understand this behavior. Could someone explain what I am observing, or what I am missing! How would I fix this, so it will work indefinitely, but will stop with the next prompt in the loop, so I can exit it manually?

UPDATE! First of all, thanks for the very good answers I received. It helped me better understand what happened. So I changed the code to reset the input var before read is called again. What I would like to achieve will be the following: 1) the "echo-hello" of the pipeline in batch 2) after the script completed the hi from the entrance to the pipe and repeats the cycle ... 3) ... I would like to be able to manually enter something else. instead, it will still be cyclically dependent on any default value that the script will set for the variable. How can I stop the pipeline and return to the stdin standard? I think this is the real problem I'm struggling with. I’m not even sure that this can be done.

Thanks and better

+5
source share
2 answers

The set /p var= command is used to extract the input data that will be stored in the specified variable. Behavior

  • If there is data to read, the data is retrieved and stored in the specified variable
  • If there is no data to read (data or all data has not been read), set /p fails (no errors) and the variable does not change .

This second case is the source of the behavior you see.

  • "Hello" (from the echo command) reads at the first iteration
  • In the second iteration, there is no data to read, but the variable does not change.

Your code (only checking the contents of the variable) does not see the difference between the two cases, since the variable still contains the data from the previous iteration.

If you don’t read any data, this is a valid case for the loop to exit, you can do something like

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

restarting the variable before each reading and leaving if the data has not been read.

It can also be written easier since

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

This uses the conditional operator || (execute the following command if the previous one did not work) to exit the loop if set /p fails.

edited to adapt to comments:

The desired behavior is to read data from the channel present in the command, but when the data has been read, then set /p should retrieve data from the user. First we need to see how the pipe works.

When cmd processes the pipe operator, two new separate processes are created to process the left and right sides of the pipe.

At this moment, we have three instances of cmd (case in the OP question):

  • cmd1 : attached to the console running the typed command
  • cmd2 : run the command on the left side of the channel
  • cmd3 : launch a batch file on the right side of the channel

I / O streams ( stdin is the input stream, stdout is the output stream) associated with each instance:

  • cmd1 : stdin - current console stdin , stdout - current console stdout
  • cmd2 : inherits the stdin console from cmd1 , stdout bound to the stdin stream in cmd3
  • cmd3 : stdin attached to the stream stdout cmd2 , stdout inherited from cmd1

In this scenario, cmd3 , which processes the batch file, is not able to get to the console stream stdin active in cmd1 or cmd2 .

So, at the moment I do not see a way to achieve this behavior.

+6
source

For your sample, this is easy to fix when you accept only one line from the pipe. I added only "set" msg = x "'to reset the default value every time to" x ", because set /p does not change the variable when there is no new input.
And set /p does not wait in the context of the channel (only quite correctly)

 @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 
+2
source

Source: https://habr.com/ru/post/1275051/


All Articles