I wanted to make a basic text editor in batch and I'm using set /p to get user input and write that to a file.
When I type in something like "hello" or "hello " it works fine, but as soon as I write something with a space and another character after than, like "hello world", the window closes.
My code was:
set /p append=Write what you want to append:
Then I tried:
set /p "append=Write what you want to append: "
which i found online, but it didn't work.
pause isn't of any help because the program crashes as soon as I press enter.
Help!
Here is the full code:
:append
set /p "append=Write what you want to append: "
if %append%==return (
cls
goto start
)
echo %append% >> %filename%
goto append
:override
cls
echo Name: %filename%
set /p override="Write new content: "
echo %override% > %filename%
pause
cls
goto start
:writefile
echo.
set /p filename=Write file name.extension:
choice /c AO /m "Append to file or override? "
if %ERRORLEVEL%==1 (
cls
echo Name: %filename%
goto append
) else (
goto override
)
Whenever the user of a batch file is prompted for a string using set /P the user has the freedom to
enter nothing at all by pressing simply just RETURN or ENTER which results in the environment variable is still not defined after prompt if it was not defined before, or the variable keeps its current value if it was already defined before, or
enter really anything including a string which could result in an exit of batch execution because of a syntax error somewhere in batch code later or an unwanted behavior.
Let us look on the line
if %append%==return (
If environment variable is not defined before with a default value and the batch user enters nothing, this line expands during preprocessing step to:
if ==return (
This command line is of course invalid and results in an exit of batch processing because of a syntax error.
Now let us assume the user enters the string:
I want to append this string.
Then the IF command line expands to:
if I want to append this string.==return (
And of course also this command line is invalid.
The other answerers suggested to enclose the two strings to compare in double quotes, i.e. use the command line:
if "%append%"=="return" (
But does that really solve all possible issues?
Is the code now really safe against any user input.
Let us look what happens if the user enters the string:
" is a double quote. It should be used around paths like "%ProgramFiles(x86)%".
The IF command line expands now to:
if "" is a double quote. It should be used around paths like "%ProgramFiles(x86)%""=="return" (
And this command line is again invalid and results in an exit of the batch file because of a syntax error.
So how to make batch code safe against really any user input?
The solution is using delayed environment variable expansion wherever the user entered string is referenced.
Example:
#echo off
setlocal EnableDelayedExpansion
echo.
echo Enter EXIT to exit this batch script.
:PromptUser
echo.
set "UserInput=Nothing^!"
set /P "UserInput=Please enter a string: "
if /I "!UserInput!" == "exit" goto EndBatch
echo You entered: !UserInput!
goto PromptUser
:EndBatch
echo.
echo Thanks for running this example batch code.
echo.
echo The batch file ends in 3 seconds ...
endlocal
%SystemRoot%\System32\ping.exe localhost -n 4 >nul
By using delayed expansion the user entered string can't result in a invalid or unexpected command line in later code. This is also important for the command line
echo !append!>>%filename%
It is important to have no space character left of >> or this space character is also written into the file as trailing space.
But delayed expansion is also important here in case of user enters for example just 2 which would result with echo %append%>>%filename% in
echo 2>>%filename%
which does not append the character 2 to the file, but would append STDERR to the file which results in an empty line written to the file.
Delayed expansion is also needed for this string entered by the user:
(Var1 > 0x2F) && (Var1 < 0x3A)
which should be written with ECHO into the file as entered and not what Windows command interpreter would produce after expanding the string when using echo %append%>>%filename%.
Change your set to put quotes in the right place around the prompt string, and your if line to put quotes around the string being tested.
set /p append="Write what you want: "
set append
echo xx%append%yy
if "%append%"=="return" (
echo Yes!
)
Your problem is with
if %append%==return (
which will give a syntax error if append contains spaces.
use
if "%append%"=="return" (
"quoting a string" makes cmd interpret it as a single element so the required syntax of if,
if string==string (
is no longer violated.
Related
So my friend gave me a batch file he wanted me to check if everything is correct, but I've got no clue what =< does in this.
:artthou_54
mode 50,20
cls
set videopath=0
title Art thou sure?
set /p videopath=<\DataTron\Ainstro\YePathToVido.dat
if %videopath%=="" echo Video Path = %videopath%
if %videopath%=="C:\Vodie" echo It seems you have the same path as the creator
set /p videopath="Would you like to open %VideoPath%? (Y/N): "
mode 50,20
if %videopath%==Y goto videohole-b
if %videopath%==N goto videohole
if %videopath%==y goto videohole-b
if %videopath%==n goto videohole
if %videopath%==back goto videohole-c
if %videopath%==exit exit
goto Art-thou-unsure
That line sets the first line of YePathToVido.dat to the variable videopath.
Normally, set /p gets input from the user and reads in until it receives a CRLF.
However, < redirects input from the specified file and sends it to the set /p command. Since set /p reads until it receives a CRLF, this effectively tells the command to read from the start of the file until the end of the first line.
On a side note, those first two if statements are incorrect because batch compares everything on both sides of the ==, including the quotes. Those lines need to change to if "%videopath%"=="" and if "%videopath%"=="C:\Vodie", respectively.
If I have the following example code:
#echo off
:menu
cls
echo 1. win
echo 2. lose
set /p menu=
goto %menu%
pause>nul
:win
cls
echo yay lolz
pause>nul
:lose
cls
echo really?
pause>nul
How do I stop the batch from quitting if I type "test" instead of a valid response?
1. Documentations for the Windows commands
I suggest bookmarking in your browser:
Microsoft's command-line reference
An A-Z Index of the Windows CMD command line (SS64)
There can be get help for each Windows command by running in a command prompt window the command with /? as parameter, for example if /?, set /?, ... The execution of help results in an output of an incomplete list of Windows commands with a brief description.
2. Usage of SET /P for user prompts
It is advisable not using set /p if the user should choose from one of several offered options. There are multiple facts which must be taken into account on prompting a user for entering a string and assigning it to an environment variable:
The environment variable MyVar is not modified on usage of set /P "MyVar=Your choice: " if the user presses intentionally or by mistake just RETURN or ENTER. This means if the environment variable MyVar is not defined already before the user prompt, it is still not defined after the user prompt finished with just hitting key RETURN. And if MyVar is defined already before the user prompt, it keeps its value unmodified in case of the user presses just RETURN or ENTER. The command SET exits with error value 1 on user did not enter a string at all as documented by What are the ERRORLEVEL values set by internal cmd.exe commands?
The user has the freedom to type any string on being prompted with set /P. The batch file author has no control on what the user really enters. So the batch file author must take into account that the user enters by mistake or intentionally a string which could result in an exit of batch file execution because of a syntax error, or it does something completely different as it is defined for.
A simple example:
#echo on
:MainMenu
#set /P "menu=Your choice: "
if %menu% == 1 goto Label1
if %menu% == 2 goto Label2
goto MainMenu
:Label1
#echo Option 1 was chosen, fine.
exit /B
:Label2
#echo Option 2 was chosen, okay.
This batch file with echo on instead of echo off at top is started from within a command prompt window for debugging purposes.
Just RETURN is pressed on user prompt on first run. Windows command interpreter exits the batch file processing because first IF condition is preprocessed before execution of IF command to:
if == 1 goto Label1
There is obviously missing the first argument. cmd.exe encounters this syntax error and exits batch processing with an appropriate error message. The reason is the missing definition of environment variable menu which is not defined before user prompt and is still not defined after user prompt.
The string 2 is entered on second run of the batch file from within command prompt window and the batch file works as expected.
On third run of the batch file from within same command prompt window again just RETURN is pressed on user prompt. The batch file outputs again the second message. Why? The environment variable menu is still defined from second batch file execution with that string and the variable was not modified on pressing RETURN.
Okay, let us modify the example batch file to:
#echo on
:MainMenu
#set "menu=2"
#set /P "menu=Your choice: "
if "%menu%" == "1" goto Label1
if "%menu%" == "2" goto Label2
goto MainMenu
:Label1
#echo Option 1 was chosen, fine.
exit /B
:Label2
#echo Option 2 was chosen, okay.
This is already better as now environment variable menu is always predefined with value 2. So if the user enters nothing, a jump to Label2 is done. Also the value of previous run of variable menu has no effect anymore on execution of the batch file.
Another solution would be making use of exit code 1 on user not entering a string at all and define only in this case the environment variable with a default value by using:
#set /P "menu=Your choice: " || set "menu=2"
Single line with multiple commands using Windows batch file describes the conditional execution operator || to run the second command set "menu=2" only if the first executed prompt command set /P "menu=Your choice: " exits with an exit code not equal 0 as done when the user does not enter anything at all.
Thanks aschipfl for this contribution.
But is that really secure and fail safe now?
No, it isn't. The user still can enter by mistake a wrong string.
For example the user enters by mistake " instead of 2 which is easy on German keyboards as CapsLock+2 or Shift+2 results in entering ". The first IF command line after preprocessing is now:
if """ == "1" goto Label1
And this is again an invalid command line resulting in an exit of batch file processing because of a syntax error.
Let us assume a user enters on prompt the string:
" == "" call dir "%USERPROFILE%\Desktop" & rem
Note: There is a space at end.
The first IF condition is preprocessed by Windows command interpreter to:
if "" == "" call dir "%USERPROFILE%\Desktop" & rem " == "1" goto Label1
It can be seen that the batch file executes now a command not written in the batch file at all on both IF conditions.
How to get a user prompt fail safe and secure?
A user prompt can be made fail safe and secure by using delayed variable expansion at least for the code evaluating the string input by the user.
#echo on
:MainMenu
#setlocal EnableDelayedExpansion
#set "Label=MainMenu"
#set /P "menu=Your choice: " || set "menu=2"
if "!menu!" == "1" set "Label=Label1"
if "!menu!" == "2" set "Label=Label2"
endlocal & goto %Label%
:Label1
#echo Option 1 was chosen, fine.
exit /B
:Label2
#echo Option 2 was chosen, okay.
Now the user input string does not modify anymore the command lines executed by Windows command processor. So an exit of batch file processing because of a syntax error caused by user input is not possible anymore (fail safe). Furthermore, the batch file never executes commands not written in batch file (secure).
3. Usage of CHOICE for a choice prompt
There is a better command than set /P for a simple choice menu – CHOICE.
#echo off
:MainMenu
cls
echo/
echo 1 ... Option 1
echo 2 ... Option 2
echo E ... Exit
echo/
%SystemRoot%\System32\choice.exe /C 12E /N /M "Your choice: "
if errorlevel 3 exit /B
if errorlevel 2 goto Label2
if not errorlevel 1 goto MainMenu
#echo Option 1 was chosen, fine.
exit /B
:Label2
#echo Option 2 was chosen, okay.
The user has no freedom anymore to enter something not defined by batch file author. The batch file continues immediately after the user has pressed either 1, 2, E or Shift+E. Everything else is ignored by choice with exception of Ctrl+C.
The dynamic variable ERRORLEVEL has with three options nearly always a value in range 1 to 3 after choice terminated with returning 1 to 3 as exit code to calling cmd.exe. The exception is the rare use case that the user of a batch file pressed Ctrl+C on prompt and answers the next prompt Terminate batch job (Y/N)? of cmd.exe with N. In this case the dynamic variable ERRORLEVEL has the value 0 which is the reason for if not errorlevel 1 goto MainMenu to handle also this very special use case.
Note: if errorlevel X means IF GREATER OR EQUAL X. So it is always necessary to start with highest possible exit code of command choice.
As the exit code assigned to ERRORLEVEL is well known, it is possible on larger menus to optimize the code further by using appropriate labels:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ERRORLEVEL="
:MainMenu
cls
echo/
echo 1 ... Option 1
echo 2 ... Option 2
echo E ... Exit
echo/
%SystemRoot%\System32\choice.exe /C 12E /N /M "Your choice: "
goto Label%ERRORLEVEL%
:Label0
rem The user pressed Ctrl+C and on next prompt N and
rem so made no choice. Prompt the user once again.
goto MainMenu
:Label1
#echo Option 1 was chosen, fine.
exit /B
:Label2
#echo Option 2 was chosen, okay.
exit /B
:Label3
The usage of command CHOICE can make choice menus very simple to code.
The third command line makes sure that there is not defined by chance an environment variable with name ERRORLEVEL which would otherwise prevent accessing the current value of dynamic variable ERRORLEVEL using with the exit code of command CHOICE using the syntax %ERRORLEVEL%.
Note: The usage of goto Label%ERRORLEVEL% is only possible with the choice menu command lines not being inside a command block starting with ( and ending with a matching ).
See also: How can I make an "are you sure" prompt in a Windows batch file?
Hint 1: There is a beep sound output by CHOICE if the user presses a not acceptable key. It is not possible to suppress that beep as there is no option offered by CHOICE to avoid output of the beep sound.
Hint 2: See also my answer on Where does GOTO :EOF return to? explaining also exit /B.
Is this the proper way to write these lines of batch code with the exceptions of spaces and unneeded percent signs?
:name
cls
echo now that we've got your color figured out, what about your name?
echo simply type the name you want for your character into the space below
echo (6 char max)
echo.
set /p %player% =
cls
echo so you want your name to be %player% ?
echo.
echo 1) yes
echo 2) no
set /p %namechoice% =
if %namechoice% == 1 goto check
if %namechoice% == 2 goto name
:check
if /I %player% == %username% (goto gamestart) else goto suprise
:suprise
The player name is not output after batch user entered it.
And the string comparisons with check and name are also not working.
The command line if %namechoice% == 1 goto check results in break of batch file execution with error message:
goto was unexpected at this time.
The main mistake is a simple syntax issue:
Environment variables are defined with just specifying the variable name without percent signs and with no space character before the equal sign.
So wrong is
set /p %player% =
set /p %namechoice% =
because those two lines are expanded during preprocessing phase before really executing the command SET to
set /p =
set /p =
in case of environment variables player and namechoice are not already defined. See Why is no string output with 'echo %var%' after using 'set var = text' on command line? for details about how to define an environment variable right. It explains also why the space character left to equal sign on variable definition becomes part of the variable name which is nearly always unwanted by batch file writer.
Such simple syntax issues can be easily seen on running a batch file without #echo off at top of the batch file or with this line modified to #echo on or commented out with ::#echo off (invalid label) or rem #echo off (remark command) from within a command prompt window by entering name of the batch file with full path in double quotes instead of double clicking on the batch file.
What makes the difference?
With #echo off the command lines are not printed into the console window after preprocessing (expanding environment variables) before really executing them. This is the wanted behavior when batch file development finished. But during development and testing of a batch file it is definitely better to get displayed what is really executed by Windows command interpreter to find coding mistakes.
On double clicking a batch file cmd.exe is started to execute the batch file with option /C for closing the console window automatically when batch file execution terminated independent on success or error of execution. This makes it not possible to see for example syntax errors output by Windows command interpreter which result in an immediate exit of batch file execution. Therefore it is advisable during batch file development to run it from within a manually opened command prompt window as in this case cmd.exe is started with option /K to keep the console window open even after batch processing finished, except the batch file uses command exit without parameter /B. This makes it possible to see also the error message of an error which caused an unexpected exit of batch processing.
Later when batch file works as expected, the first line can be #echo off again and of course the batch file can be started with a double click. But during batch file development it is definitely better to always run the batch file from within a command prompt window. The up/down arrow keys can be used to scroll through the list of entered strings which makes it also possible to re-enter for example the player name easily again.
Here is the batch code rewritten with several improvements and comments:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Define a too long player name before prompting the user for the player
rem name. This too long player name is kept in case of user hits just the
rem key RETURN or ENTER without entering anything at all. Then test for
rem entered name has not more than 6 characters. Delayed expansion is used
rem as the user could enter characters like " ! % ... which would in further
rem batch code execution result in exiting batch processing because of syntax
rem error or in unexpected behavior on referencing player name with expansion
rem before running the command.
:PromptForName
cls
echo Now that we've got your color figured out, what about your name?
echo Simply type the name you want for your character into the space
echo below (6 char max).
echo/
set "Player=No name entered"
set /P "Player=Player name: "
if not "!Player:~6!" == "" goto PromptForName
echo/
echo/
echo 1) yes
echo 2) no
echo/
choice /C:12 /N "So you want your name to be !player!? "
if errorlevel 2 goto PromptForName
if /I "!player!" == "%USERNAME%" goto GameStart
echo Surprise
endlocal
goto :EOF
:GameStart
echo/
echo Okay !Player!, let's play^^!
rem Wait 3 seconds using PING instead of TIMEOUT before exiting the
rem batch file because the command TIMEOUT does not exist on Windows XP.
%SystemRoot%\System32\ping.exe 127.0.0.1 -n 4 >nul
endlocal
The comment at top explains why the environment variable Player is defined with value No name entered. The batch user has the freedom to hit just RETURN or ENTER without entering anything at all or hits by mistake one of those 2 keys before entering a name. In this case the environment variable Player is either still not defined if not defined before, or it keeps its current value if already defined before. It is not good if the user enters nothing and the environment variable Player is not defined in this case. Therefore the player name is predefined with an invalid name.
The length of the entered player name is also tested on being too long.
And the string entered by the user could contain batch syntax critical characters like a double quote, a percent sign, a redirection operator character (angle bracket, pipe), an ampersand, or with delayed expansion enabled an exclamation mark. To prevent an exit of batch processing caused by a syntax error by entered player name on using environment variable expansion before command line execution, the environment variable Player is referenced everywhere with usage of delayed expansion enabled at top of the batch file.
For printing a blank line it is better to use echo/ instead of echo. because echo. could fail and is a little bit slower because of Windows command interpreter searches for a file matching the pattern echo.* as documented in DosTips forum article ECHO. FAILS to give text or blank line - Instead use ECHO/.
The command CHOICE is much better than set /P VariableName=Prompt text if the user has to enter specific keys. The command CHOICE does not allow that the user enters something not wanted by batch file writer and is therefore much safer for a choice menu.
The account name of current user referenced with %USERNAME% could contain also a space character. Therefore it is highly recommended to enclose the entire string containing %USERNAME% always in double quotes.
"%USERNAME%" on right side of a string comparison requires that the string on left side is also enclosed in double quotes because command IF compares the two strings with including the double quotes.
For that reason the condition
if /I !player! == "%USERNAME%"
would be only true if the batch file user would have entered the player name with double quotes which is very unlikely. The double quotes must be also used on left side.
The number of space characters around the two compared strings enclosed in double quotes or not enclosed in double quotes does not matter.
Executing in a command prompt window the following batch file
#echo on
#setlocal EnableExtensions EnableDelayedExpansion
#set "Player=<|>"
if /I "!Player!"=="%SystemRoot%" echo Strings are equal.
if /I "!Player!" == "%WinDir%" echo Strings are equal.
if /I "!Player!" == "%Player%" echo Strings are equal.
if /I "!Player!"== "!Player!" echo Strings are equal.
if /I !Player! == !Player! echo Strings are equal.
#endlocal
results in the output
if /I "!Player!" == "C:\WINDOWS" echo Strings are equal.
if /I "!Player!" == "C:\WINDOWS" echo Strings are equal.
if /I "!Player!" == "<|>" echo Strings are equal.
Strings are equal.
if /I "!Player!" == "!Player!" echo Strings are equal.
Strings are equal.
if /I !Player! == !Player! echo Strings are equal.
Strings are equal.
It can be seen that the space characters around comparison operator == do not matter on execution of command IF. The Windows command processor formats the command lines pretty before executing the IF commands.
But a space character in a string to compare requires the usage of double quotes because otherwise an exit of batch processing occurs most likely because of a syntax error on batch file execution.
Note: The equal operator == of command IF is handled different than the assignment operator = of command SET. Don't mix them.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
choice /?
cls /?
echo /?
endlocal /?
goto /?
if /?
ping /?
rem /?
set /?
setlocal /?
And see also the Microsoft article Using command redirection operators for an explanation of >nul.
I have been writing some batch files now-a-days. I am beginner !. So i have made a custom batch in which by entering a setup name it launches it but. I'am having some problem creating this custom file.
#echo off
set /p lnk="Setup Name = "
if "%lnk%"=="install.itunes.x64.windows" goto itunes
:itunes
start=(path)(setup.exe).....
cls
But if a user enters "itues or "installitunes" or "KJEWBFciou" whatever that don't matchs my custom command I want a error Pop-up in this condition.
What can i Do?
and don't ask to put "if not "%lnk%" i have already tried help level:0
Because i have many setups like itunes if input will not equal to custom command it launches the next setup.
Please help me
Please igonre my errors i only made 'em here not in batch file.
in line 2 %lnk% , lnk
and line 3 "%lnk" ,"%lnk%"
Ok so..
1 #echo off
2 set /p %lnk%="Setup Name = "
3 if "%lnk%=="install.itunes.x64.windows" goto itunes
4 :itunes
5 start=(path)(setup.exe).....
6 cls
A few errors but you're close.
In line 2, you use:
set /p %lnk%=="Setup Name = " goto itunes
When setting a variable you can't use %% around it, but thats only used when comparing, because when creating the variable, the computer will replace [set /p %lnk%=] with [set /p =] which is invalid syntax.
In line 3:
if "%lnk%=="install.itunes.x64.windows" goto itunes
You never closed the quotes on the left of the '==' comparison. Do note you can also use [if %val1% equ %val2%] to the same results, which can help when you want to use other comparison tags.
A sidenote for the task you have set, although [goto itunes] works fine, its a good habit to use [goto :itunes] instead, and if you want to keep your code all together, you can just make a code block like:
if %val1% equ %val2% (
rem do stuff here
)
do note, if you either want a task to run if variables match, and if not try the next match, you can use multiple of these. Otherwise you can use:
if %val1% equ %val2% (
rem do stuff here
) else (
rem do other stuff here
)
In response to your issue on it launching the next command, thats because in line 3, you check if the variable matches your string, but if it doesnt batch skips it and runs the next line, which is your :itunes label.
All in all, this should work better, after you fix [start=(path)(setup.exe).....] to launch as desired.
#echo off && color f0 && title Itunes
:top
cls
set /p lnk="Setup Name = "
if "%lnk%"=="install.itunes.x64.windows" (
start=(path)(setup.exe).....
cls
)
cls
echo "%lnk%" was not matched to any choices...
pause
goto :top
:: _Arescet
I'm writing my own simple system allowing me to automatically sign APKs before they are uploaded to GPlay. I've got a batch file that does the signing; and a "wrapper" batch file, the content of which will be run on the command line by Jenkins post-build.
sign_apks.bat:
#echo off
set /p job= "Enter job name: "
set /p alias= "Enter key alias: "
set /p mobile= "Sign mobile? (y/n): "
set /p wear= "Sign wear? (y/n): "
echo.
echo "%job%"
echo "%alias%"
echo "%mobile%"
echo "%wear%"
[the rest of the code is sensitive so is emitted, but these variables are used later]
wrapper code:
#echo off
(echo test
echo test
echo y
echo y)| call sign_apks.bat
This article showed me how to pipe values into a program. To quote from the answer,:
Multiple lines can be entered like so:
(echo y
echo n) | executable.exe
...which will pass first 'y' then 'n'.
However, this doesn't work. This is the output I get when running the wrapper code:
Enter job name: Enter key alias: Sign mobile? (y/n): Sign wear? (y/n):
"test "
""
""
""
Help?
There is something very odd with how SET /P interacts with your piped input that I do not fully understand.
But I do have some solutions :-)
The simplest solution is to write your responses to a temporary file, and then use that temp file as redirected input.
#echo off
(
echo test
echo test
echo y
echo y
)>responses.temp
call sign_apks.bat <responses.temp
delete responses.temp
That is how I would solve your problem. But some people do not like to use temporary files (why I don't know). So I decided I would attempt to solve it using a pipe without a temp file.
I discovered an odd variation of your code that almost solves the problem - but it appends an extra unwanted space at the end of each value.
#echo off
(
call echo test1
call echo test2
call echo y1
call echo y2
) | sign_apks.bat
--OUTPUT--
Enter job name: Enter key alias: Sign mobile? (y/n): Sign wear? (y/n):
"test1 "
"test2 "
"y1 "
"y2 "
I cannot explain why the CALL enables each of the SET /P statements to work properly. But I can explain why the space is appended to each value. It has to do with why CALL is not needed when you use a batch script with a pipe.
Each side of a pipe is executed in a brand new cmd.exe session. For example, the right side of the pipe becomes a command that looks something like:
C:\Windows\system32\cmd.exe /S /D /c" sign_apks.bat"
This is the reason why CALL is not needed - control will return after the new cmd.exe session terminates.
The unwanted spaces are an artifact of how pipes process parenthesized blocks. The parser must capture the entire piped code block and transform it into a single line that can be incorporated into the CMD.EXE /C argument. The CMD.EXE parser does this by putting an & between each command. Unfortunately, the parser also inserts some extra spaces. So the left side of the pipe is transformed into something like:
C:\Windows\system32\cmd.exe /S /D /c" ( call echo test & call echo test & call echo y & call echo y )"
Now you can easily see where the unwanted trailing spaces are coming from. See Why does delayed expansion fail when inside a piped block of code? for more information about how pipes are implemented.
I finally came up with one more solution. I created a helper batch script called WriteArgs.bat that simply ECHOs each argument passed to it.
WriteArgs.bat
#echo off
:loop
if .%1 equ . exit /b
echo %1
shift /1
goto loop
With this simple batch script, you can now solve your problem using:
WriteArgs.bat test test y y | sign_apks.bat
Again, I don't understand why SET /P works properly here, yet doesn't work with your original command. But this does solve the problem :-)
Update - Well, it solves the problem on my machine. But it seems to be a timing issue, and I don't have confidence that any given piped solution will always work. The only solution I feel is robust is the one that uses a temp file and redirection.
You know, the easiest solution would be to supply job, alias, mobile, and wear as script arguments rather than trying to pipe them into stdin. You can still set /p if not defined, if you wish to run interactively without arguments.
#echo off
setlocal
set "job=%~1"
set "alias=%~2"
set "mobile=%~3"
set "wear=%~4"
if not defined job set /p "job=Enter job name: "
if not defined alias set /p "alias=Enter key alias: "
if not defined mobile set /p "mobile=Sign mobile? (y/n): "
if not defined wear set /p "wear=Sign wear? (y/n): "
echo.
echo "%job%"
echo "%alias%"
echo "%mobile%"
echo "%wear%"
Then when you call sign_apks.bat, just call it like this:
call sign_apks.bat test test y y
It's a problem of set /p, it reads the input buffer, but it fails to split this buffer when multiple lines are available, it simply takes the first line from the buffer and the rest will be discarded.
This isn't a problem for a single echo piped to a single set/p, but when you pipe more lines to multiple set/p you got random results.
The solution of dbenham can work, but it's depends on your system!
As both processes (line producer and the set/p consumer) are asnchronously running in an own cmd.exe task, it depends on the cpu time each process gets.
But you can ensure a correct consuming by splitting the content by another program like more or findstr.
As these split the input buffer proberly at the line boundarys.