Piping multiple values into a program in a batch script - batch-file

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.

Related

set /p is not working as expected

I am trying to create the interactive bat file which ask for the folder details it is able to take the userchoice but after that it is not able to set the folder path given by useri.e. it takes the path till Desktop only.Below is my code for the same:
#echo off
set /p UserInfo= "Do you have abc software(y/n)? "
echo %UserInfo% // here it is working as expected
IF %UserInfo%==y (
echo "Reply is true-----"
set /p Path= "Please enter path for abc directory? "
echo %Path% //but here it takes the path till the desktop only(C:\user\username\Desktop)
CD %Path%
dir
set /p Path1= "Please enter path1 directory path provided in package? "
echo %Path1% //but here it takes the path till the desktop only(C:\user\username\Desktop)
CD %Path1%
)
IF %UserInfo%==n (
echo "Reply is False**************"
)
pause
How to read the folder directive?
Hmm.. Please use the search bar as user Magoo, LotPings said.
Also, Stephan & Squashman mentioned, do not set a variable with the name path because there is a internal variable named path. If you rename it, other programs may not work properly.
What's DelayedExpansion?
When batch files are run, cmd process them line by line. The entire if statement get processed at once. That's why the variable are un-set.
Since we want cmd to process those variable at run-time, we will need to tell it to do so, by adding setlocal enableDelayedExpansion. This enables run-time variable expansion. To disable, just change enable to disable.
You may want to add it like so:
#echo off
setlocal enableDelayedExpansion
rem your code follows...
How To Make Variable Get Processed Run-Time?
Simply change %var% to !var!.
Please note that for loop metavariable %%n cannot be changed to !!n, since itself already implicated a delayed expansion.
Command-line arguments %n cannot be changed to !n. You may want to do this instead:
if "%var%"=="abc" (
set variable=%1
echo !variable!
)
SET /P Code Injection Cause Security Issue?!
If the input of %Userinput% is a==a format D:\ && echo, cmd sees:
if a==a
do format D:\
do echo ==y (
Which... formats your D drive. Adding quotes like if "%var%"=="abc" won't help since user can just escape the quote and execute the commands.
See here for more info.
SET /P Alternatives
You may want to consider CHOICE for single letter choice. It's command syntax is like so:
choice /c choices /n /cs /t timeout /d default_choice /m prompt
/n hides the list of options, letting /m to display it's own prompt
/cs == case-insensitive.
PATH Variable
Again mentioned above, PATH is a internal variable used by Windows and other programs. Mis-setting it may cause some Windows functions or programs to stop functioning properly.
Instead, use another variable name like programPath.

set /p with spaces in variable value

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.

cmd, write and read from txt

i was toying around with cmd a bit and wanted to write a little application which involves a simple feature to read a counter from a txt file, then work with it and at the end raise the counter by one.
set /p x=<file.txt
...
set /a y=x+1
echo %y%>file.txt
Problem is it always returns "ECHO ist eingeschaltet (ON)." which translates to ECHO is turned on (ON) for some reason. Could somebody please explain where it comes from and how to fix it? I dont need anything fancy. I just want it to work and know where my mistake is.
At first, I want to show you how your echo command line should look like:
> "file.txt" echo(%y%
Here is your original line of code again:
echo %y%>file.txt
The reason for the unexpected output ECHO is on./ECHO is off. is because the echo command does not receive anything to echo (type echo /? and read the help text to learn what on/off means). Supposing y carries the value 2, the line expands to:
echo 2>file.txt
The number 2 here is not taken to be echoed here, it is consumed by the redirection instead; according to the article Redirection, 2> constitutes a redirection operator, telling to redirect the stream with the handle 2 (STDERR) to the given file. Such a handle can reach from 0 to 9.
There are some options to overcome that problem:
inserting a SPACE in between the echoed text and the redirection operator:
echo %y% >file.txt
the disadvantage is that the SPACE becomes part of the echoed text;
placing parentheses around the echo command:
(echo %y%)>file.txt
placing the redirection part at the beginning of the command line:
>file.txt echo %y%
I prefer the last option as this is the most general and secure solution. In addition, there is still room for improvement:
quote the file path/name to avoid trouble in case it contains white-spaces or other special characters;
use the odd syntax echo( to be able to output everything, even an empty string or literal strings like on, off and /?;
> "file.txt" echo(%y%
Hint:
To see what is actually going on, do not run a batch file by double-clicking on its icon; open a command prompt window and type its (quoted) path, so the window will remain open, showing any command echoes and error messages. In addition, for debugging a batch file, do not put #echo off on top (or comment it out by preceding rem, or use #echo on) in order to see command echoes.
Echo on means that everything that is executed in the batch is also shown in the console. So you see the command and on the following line the result.
You can turn this off with the echo off command or by preceding a # sign before the command you want to hide.
so
::turns of the echo for the remainder of the batch or untill put back on
::the command itself is not shwn because off the #
#echo off
set /p x=<file.txt
...
::the following won't be shown regardless the setting of echo
#set /a y = x+1
echo %y% > file.txt
EDIT after first comment
because your command echo %y%>file.txt doesn't work, you need a space before the > symbol, now you get the result of echo which gives you the current setting of echo
here a working sample, I put everything in one variable for sake of simplicity.
echo off
set /p x =< file.txt
set /a x += 1
echo %x% > file.txt

If condition is not equal batch file crashes

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

DOS echo %variable% "Echo is ON"

I have a dos batch file.
mycommand.exe>c:\temp
find /B Serv c:\temp>c:\temp2
set /p var1=<c:\temp2
SET var2=%var1:~-7%
echo %var2%
This is only DOS, not windows environment.
The problem is that, the batch file output is:
"Echo is ON". Can't echo the VAR2 variable.
mycommand.exe is a simple app. Not important.
> type c:\temp"
VERSION 45.2
TAG1 NUMBER is 1234567
Serv NUMBER is 9654754
> type c:\temp2
c:temp Serv NUMBER is 9654754
What can I do, If I would like echo the VAR2 variable?
I can't use "setlocal enabledelayedexpansion" because setlocal "Command or filename not recognized".
Edit: What am I want exactly?
I would like to ECHO mycommand.exe output 3rd line last 7 characthers. Thats all.
"good old DOS" is old, but not very good.
Your problem can be solved, basicly by building a temporary .bat file.
It's described in detail here:
https://support.microsoft.com/en-us/kb/66292
I have no DOS available, so I can't test, but this should work for you:
mycommand.exe>c:\temp.txt
find "Serv " c:\temp.txt>c:\temp2.txt
REM init.txt should already exist
REM to create it:
REM COPY CON INIT.TXT
REM SET VARIABLE=^Z
REM ( press Ctrl-Z to generate ^Z )
REM
REM also the file "temp2.txt" should exist.
copy init.txt+temp2.txt varset.bat
call varset.bat
for %%i in (%variable%) do set numb=%%i
echo Server number is: %numb%
REM just because I'm curious, does the following work? :
set var2=%variable%
echo var2 is now %var2%
The manual creation of init.txt has to be done once only, if you can live with, that it always creates a variable with the same name (therefore the last two lines, so you could use the same init.txt over and over again - please feedback, whether it works - I'm quite curious)

Resources