Batch: Pipe command output to variable and compare - batch-file

I try to pipe my command output to variables and then compare if those variables have the same value. However it always return both variables have same value(even though it is not). Below is my code:
#echo off
goto main
:main
setlocal
echo 111 | (set /p readvalue= & set readvalue)
echo 123 | (set /p readvaluebash= & set readvaluebash)
if "%readvalue%"=="%readvaluebash%" goto valid
if NOT "%readvalue%"=="%readvaluebash%" goto invalid
:valid
echo yes
pause
goto finish
:invalid
echo no
pause
goto finish
:finish
endlocal
I always get the yes result. Anyone know my mistake here? Thanks in advance!

When you run (same for the other line)
echo 111 | (set /p readvalue= & set readvalue)
you see that the value shown in console as the variable value is 111, so the set /p was able to retrieve the piped data.
The problem is that the pipe operator starts two separate cmd instances: one running the left part (echo) of the pipe and another running the right part (set /p).
As each process has its own environment space and as the set /p is executed in a separate cmd instance, any change to any variable in this new cmd instace will not change the environment of the cmd instance running the batch file.
To store the output of a command in a variable, instead of a pipe you can use a for /f command
for /f "delims=" %%a in ('echo 111') do set "readValue=%%a"

Related

How to show what user typed in this command?

please help! I was looking for the answer all over the internet.
Here's my code:
#echo off
title var test
:question
set a1=This
set a2=Is
set a3=a
set a4=Var
set a5=Test
choice /c 12345 /m "press a number"
if errorlevel=5 set num=5&goto answer
if errorlevel=4 set num=4&goto answer
if errorlevel=3 set num=3&goto answer
if errorlevel=2 set num=2&goto answer
if errorlevel=1 set num=1&goto answer
:answer
echo now change the answer.
set /p a%num%=
FOR /F "tokens=1-6" %%1 IN ("%a1% %a2% %a3% %a4% %a5% a%num%") DO echo %%1 %%2 %%4 %%5.&echo You typed=%%6
pause
goto question
As you can see I made the user select a number between 1 and 5 to change the specific word. But when I try same kind of code to show what he typed doesn't work :(
Environment variables should never begin with a digit and using a digits for loop variables should be also avoided. Run in a command prompt window call /? and output is the help for this command explaining how batch file arguments can be referenced with %0, %1, %2, ... which explains why environment variables with digit as first character and loop variables with a digit are not good in general even on being
a%num% in set of FOR does not reference the value of environment variable a1 or a2 or a3 or a4 or a5. It is just the name of the environment variable. The for loop is not necessary at all.
#echo off
title var test
:question
set "a1=This"
set "a2=Is"
set "a3=a"
set "a4=Var"
set "a5=Test"
%SystemRoot%\System32\choice.exe /C 12345E /N /M "Press a number in range 1-5 or E for exit: "
if errorlevel 6 goto :EOF
set "num=%ERRORLEVEL%"
set /P "a%num%=Now change the answer: "
echo %a1% %a2% %a3% %a4% %a5%.
call echo You typed: %%a%num%%%
pause
goto question
The command line call echo You typed: %%a%num%%% is parsed by Windows command processor before execution of the command line on number 3 entered to call echo You typed: %a3%. This command line is parsed a second time because of command call resulting in replacing %a3% by the value of environment variable a3 and so echo outputs the expected string.
It would be also possible to replace call echo You typed: %%a%num%%% by
setlocal EnableDelayedExpansion
echo You typed: !a%num%!
endlocal
The usage of delayed environment variable expansion results also in double parsing the command line before execution of command echo. For more details see How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Please read also this answer for details about the commands SETLOCAL and ENDLOCAL.
The two lines below in batch code above are also not really good taking into account that the user can really enter anything.
echo %a1% %a2% %a3% %a4% %a5%.
call echo You typed: %%a%num%%%
For example if the user enters number 1 and on next prompt enters:
Your user name is:& setlocal EnableDelayedExpansion & echo !UserName!& endlocal & rem
Then the batch file does something completely different than designed for and outputs the user's account name.
Secure would be the batch code:
#echo off
title var test
setlocal EnableExtensions DisableDelayedExpansion
:question
set "a1=This"
set "a2=Is"
set "a3=a"
set "a4=Var"
set "a5=Test"
%SystemRoot%\System32\choice.exe /C 12345E /N /M "Press a number in range 1-5 or E for exit: "
if errorlevel 6 goto :EOF
set "num=%ERRORLEVEL%"
set /P "a%num%=Now change the answer: "
setlocal EnableDelayedExpansion
echo !a1! !a2! !a3! !a4! !a5!.
echo You typed: !a%num%!
endlocal
pause
goto question
Now the user input string cannot modify anymore the command lines executed by Windows command processor.
A solution with the useless FOR loop would be:
setlocal EnableDelayedExpansion
for /F tokens^=1-6^ eol^= %%A in ("!a1! !a2! !a3! !a4! !a5! !a%num%!") do echo %%A %%B %%C %%D %%E.&echo You typed: %%F
endlocal
eol= is necessary to output everything correct also if use enters number 1 and next a string starting with a semicolon. The FOR options string cannot be enclosed in double quotes in this case like "tokens=1-6 eol=" because of this would define " as end of line character and nothing is output if user enters number 1 and enters next a string starting with ". The equal sign and the space must be escaped with ^ to be interpreted as literal characters by cmd.exe on double parsing the entire for command line before execution of command for.
Note: The FOR loop solution does not work correct on user enters for first variable value the special command line string as posted above. So it is also not really secure.

What to fix on my code with two IF conditions inside a FOR loop referencing an environment variable defined by the IF conditions?

Could you please guide me how to fix my script to get the trace route results to some IP addresses?
I want to get the tracert results for 8.8.8.8 and 8.8.4.4. I wrote the script below and it works fine:
#echo off
echo %date% %time%
SET DNS1=8.8.8.8
SET DNS2=8.8.4.4
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
echo tracert to %%i
tracert -d %%i
)
echo %time%
exit
I want to add a function so that it can print host name automatically. I tried as below, but it doesn't work.
#echo off
echo %date% %time%
SET DNS1=8.8.8.8
SET DNS2=8.8.4.4
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
if (%%i == 8.8.8.8 set host=Google1
%%i == 8.8.4.4 set host=Google2)
echo tracert to %host%
tracert -d %%i
)
echo %time%
exit
Could somebody please correct the code for me?
The proper use to test multiple conditions using an IF command is used in this code. As a best practice I always use quotes around string comparisons. Also note the use of delayed expansion with the host variable. This is required because you are creating a variable inside a parenthesized code block.
#echo off
setlocal enabledelayedexpansion
echo %date% %time%
SET DNS1=8.8.8.8
SET DNS2=8.8.4.4
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
if "%%i"=="8.8.8.8" set host=Google1
if "%%i"=="8.8.4.4" set host=Google2
echo tracert to !host!
tracert -d %%i
)
echo %time%
endlocal
exit
The batch file could be written like this:
#echo off
echo %DATE% %TIME%
setlocal EnableExtensions EnableDelayedExpansion
set "DNS1=8.8.8.8"
set "DNS2=8.8.4.4"
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
if %%i == 8.8.8.8 set "host=Google1"
if %%i == 8.8.4.4 set "host=Google2"
echo tracert to !host!
tracert -d %%i
)
endlocal
echo %TIME%
exit /B
Windows command processor executes one command line after the other. How a command line is parsed before execution is described at How does the Windows Command Interpreter (CMD.EXE) parse scripts? What is executed finally after parsing a command line can be seen on executing a batch file without #echo off from within a command prompt window as described at debugging a batch file.
A command block starting with ( and ending with matching ) is parsed completely by cmd.exe before the command is executed with makes conditionally or unconditionally use of the command block. During parsing the command block all environment variable references using syntax %variable% like %host% are substituted by current value of the referenced environment variable. In this case %host% is most likely replaced by an empty string before FOR is executed at all if the environment variable host is not defined by chance on starting the batch file.
The help output on running set /? in a command prompt window explains when and how to use delayed expansion on an IF and a FOR example. In code above delayed environment variable expansion is enabled and used to reference the current value of environment variable host on iterations of the loop body command block on which the environment variable is defined and gets assigned a string value.
There would be also possible to use:
#echo off
echo %DATE% %TIME%
setlocal EnableExtensions DisableDelayedExpansion
set "DNS1=8.8.8.8"
set "DNS2=8.8.4.4"
for %%i in (%DNS1% %DNS2%) do (
echo ----------------
if %%i == 8.8.8.8 set "host=Google1"
if %%i == 8.8.4.4 set "host=Google2"
call echo tracert to %%host%%
tracert -d %%i
)
endlocal
echo %TIME%
exit /B
The command line call echo tracert to %%host%% is modified during the parsing step of entire command block by Windows command processor to call echo tracert to %host% and because of command CALL this command line is parsed a second time on each execution of the command block before ECHO command is executed resulting in printing the current value of environment variable host to console window.
But I suggest to use this much better code:
#echo off
echo %DATE% %TIME%
echo/
setlocal EnableExtensions DisableDelayedExpansion
set "DNS1=8.8.8.8=Google1"
set "DNS2=8.8.4.4=Google2"
for /F "tokens=2* delims==" %%I in ('set DNS 2^>nul') do (
echo ----------------
echo tracert to %%J
echo %SystemRoot%\System32\tracert.exe -d %%I
)
endlocal
echo/
echo %TIME%
exit /B
One or more IP addresses and their host names are assigned to one or more environment variables starting with the string DNS. IP address and host name are separated by an equal sign.
The command FOR runs in a separate command process started with cmd.exe /C in background the command line:
set DNS 2>nul
Command SET outputs to handle STDOUT (standard output) all environment variables starting with DNS sorted alphabetically with name=value which means for this example:
DNS1=8.8.8.8=Google1
DNS2=8.8.4.4=Google2
The error message output by SET to handle STDERR (standard error) on not finding any environment variable starting with the string DNS would be suppressed by this code by redirecting it to device NUL.
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded set command line with using a separate command process started in background.
FOR with using option /F captures everything output to handle STDOUT in background command process and processes this output by ignoring empty lines and lines starting with a semicolon.
The captured lines start all with DNS and so there is definitely no line ignored as no line starts with a ;.
FOR with using option /F would also split each line into substrings using space/tab as delimiter with assigning just first substring to specified loop variable I. But this line splitting behavior is not useful for this task.
For that reason the option string "tokens=2* delims==" redefines the line splitting behavior. Now = is used as delimiter between the strings instead of space and tab.
And instead of assigning the first = delimited string to loop variable I which would be the name of the environment variable, the second equal sign delimited string is assigned to loop variable I because of tokens=2 which is the IP address.
And there is additionally assigned to next loop variable J according to ASCII table the rest of the line after the equal sign(s) after second = delimited string without any further string splitting on an equal sign. So the host name is assigned to loop variable J, even on containing one or more = as long as not containing them at beginning of host name.
This code is obviously better as the DNS environment variables can be defined all at top of the batch file with IP address and host name and nothing must be changed on the command lines below because it simply processes from 0 to n environment variables starting with DNS and having at least a second = delimited substring.
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.
call /?
echo /?
endlocal /?
exit /?
for /?
if /?
set /?
setlocal /?
tracert /?

.Bat code only creates file for one set of data entered

I want to create a bat file asking for a user input which will ask for some choices:
#echo off
MKDIR D:\BatFiles\File
SET /P Output="D:\BatFiles\File"
ECHO Select Task
ECHO ==========
#echo off
title Task List Creator
:homescreen
ECHO
Echo 1.) Create Notepad Task File
Echo 2.) Exit
Echo.
set /p input=Type Choice :
if "%input%"=="1" goto getInfo
if "%input%"=="2" exit
Pause
:getInfo
set /p VarOne=Enter Type:
set /p VarTwo=Enter Number:
set /p VarThree=Enter Name:
echo Task Type=%VarOne% >> %Output%\test.txt
echo Task Number=%VarTwo% >> %Output%\test.txt
echo Task Name=%VarThree% >> %Output%\test.txt
echo Entry successfully written
Pause
goto finished
:finished
echo Do you want to create a new set of entry?
set /p response= Y or N?
if "%response%"=="Y" goto getInfo
if "%response%"=="N" goto homescreen
--The problem with this code is that I want to create more than 2 entries. This code only creates an output file if user has only one set of entries. If user creates 2 or more, the output file is not created and data entered appears only when user runs the bat file again and only enters one set of data. Sorry about the lame question, I'm just a batch file beginner here.
Look on this code:
#echo off
title Task List Creator
setlocal EnableExtensions EnableDelayedExpansion
set "OutputFolder=D:\BatFiles\File"
set FileNumber=0
:HomeScreen
cls
echo Select Task
echo ===========
echo.
echo 1 ... Create Notepad Task File
echo 2 ... Exit
echo.
set "Input=2"
set /P "Input=Your choice: "
if "!Input!"=="1" goto PrepareTaskFile
if "!Input!"=="2" endlocal & goto :EOF
goto HomeScreen
:PrepareTaskFile
set /A FileNumber+=1
set "OutputFile=%OutputFolder%\test%FileNumber%.txt"
if exist "%OutputFile%" del "%OutputFile%"
:GetInfo
echo.
set "VarOne="
set "VarTwo="
set "VarThree="
:EnterType
set /P "VarOne=Enter type: "
if not defined VarOne goto EnterType
:EnterNumber
set /P "VarTwo=Enter number: "
if not defined VarTwo goto EnterNumber
:EnterName
set /P "VarThree=Enter name: "
if not defined VarThree goto EnterName
if not exist "%OutputFolder%" mkdir "%OutputFolder%"
echo Task Type=!VarOne!>>"%OutputFile%"
echo Task Number=!VarTwo!>>"%OutputFile%"
echo Task Name=!VarThree!>>"%OutputFile%"
echo.
echo Entry successfully written.
echo.
pause
echo.
echo Do you want to create a new set of entry?
echo.
set "Response=N"
set /P "Response=Y or N? "
if /I "!Response!"=="Y" goto GetInfo
goto HomeScreen
The environment variable on prompt keeps its current value if the user just hits RETURN or ENTER. Therefore it is advisable to define a default value or undefine a variable before prompting the user.
The entered strings assigned to the variables are referenced with usage of delayed expansion in case of user enters something not expected which could result in a syntax error and therefore exit of batch processing on referencing the entered strings with immediate expansion. For example a string comparison would fail with a syntax error if the user enters a string with a double quote.
See How to set environment variables with spaces? why using double quotes as it can be seen here on set "variable=string value" and set /P "variable=prompt text".
The space character left of redirection operator >> in code of question would be also written into the file. This should be avoided by removing it and reference the variables with delayed expansion in case of the variable value is a number with value 1, 2, 3, ... to avoid a wrong handle redirection, see the Microsoft article about Using command redirection operators.
On usage of set /P for a menu instead of command choice it must be always taken into account that the user enters something not suggested. So if the user enters on first prompt whether 1 nor 2, there must be code which defines the behavior on the invalid input.
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.
cls /?
del /?
echo /?
endlocal /?
goto /?
if /?
mkdir /?
pause /?
set /?
setlocal /?
title /?
See also answer on Single line with multiple commands using Windows batch file for an explanation of & between endlocal and goto :EOF.

How to pass variables from one batch file to another batch file?

How do I write a batch file which gets an input variable and sends it to another batch file to be processed.
Batch 1
I don't know how to send a variable to batch 2 which is my problem here.
Batch 2
if %variable%==1 goto Example
goto :EOF
:Example
echo YaY
You don't need to do anything at all. Variables set in a batch file are visible in a batch file that it calls.
Example
test1.bat
#echo off
set x=7
call test2.bat
set x=3
call test2.bat
pause
test2.bat
echo In test2.bat with x = %x%.
Output
... when test1.bat runs.
In test2.bat with x = 7.
In test2.bat with x = 3.
Press any key to continue . . .
You can pass in the batch1.bat variables as arguments to batch2.bat.
arg_batch1.bat
#echo off
cls
set file_var1=world
set file_var2=%computername%
call arg_batch2.bat %file_var1% %file_var2%
:: Note that after batch2.bat runs, the flow returns here, but since there's
:: nothing left to run, the code ends, giving the appearance of ending with
:: batch2.bat being the end of the code.
arg_batch2.bat
#echo off
:: There should really be error checking here to ensure a
:: valid string is passed, but this is just an example.
set arg1=%~1
set arg2=%~2
echo Hello, %arg1%! My name is %arg2%.
If you need to run the scripts simultaneously, you can use a temporary file.
file_batch1.bat
#echo off
set var=world
:: Store the variable name and value in the form var=value
:: > will overwrite any existing data in args.txt, use >> to add to the end
echo var1=world>args.txt
echo var2=%COMPUTERNAME%>>args.txt
call file_batch2.bat
file_batch2.bat
#echo off
cls
:: Get the variable value from args.txt
:: Again, there is ideally some error checking here, but this is an example
:: Set no delimiters so that the entire line is processed at once
for /f "delims=" %%A in (args.txt) do (
set %%A
)
echo Hello, %var1%! My name is %var2%.
If you are reading the answers and still getting problems, you may be using setlocal wrong.
setlocal works like namespaces with endlocal closing the last opened namespace.
If you put endlocal at the end of your callee as I used to do by default, your variable will be lost in the previously opened setlocal.
As long as your variable is set in the same "local" as your caller file's you will be ok.
Here are 2 ways. One writes to a file while the other does not.
All include: setlocal EnableDelayedExpansion
Batch1: (.bat or .cmd)
set myvar1=x 1920
set myvar2=y 1080
set myvar > MyConfig.ini
REM (Use >> to append file and preserve existing entries instead of overwriting.)
MyConfig.ini will be created/appended containing:
myvar1=x 1920
myvar2=y 1080
Batch2:
for /f "delims== tokens=1,2" %%I in (myconfig.ini) do set %%I=%%J
Then, myvar1 and myvar2 will be set to their stored values.
To avoid any disk file storage, try something I cooked up:
Batch1:
(may contain:) start /b cmd /c Batch2.bat
title set myvar1=x 1920^& set myvar2=y 1080
Batch2:
for /f "tokens=*" %%G in ('gettitle.exe') do (set titlenow=%%G)
%titlenow%
The result is that Batch2 runs the commands...
set myvar1=x 1920& set myvar2=y 1080
...to replicate the variables from Batch1.
The vars might then be changed and returned to Batch1 in the same manner.
It's ideal for parallel processing AI apps on supercomputers running Windows 10! lol
gettitle.exe is from http://www.robvanderwoude.com
You can find out the solution from below-
variable goes here >> "second file goes here.bat"
What this code does is that it writes the variables to the second file if existing.Even if it does not exist, it will create a new file.

taking and writing variables for a batch file

Basically here's what I want: A batch file that prompts the user to set a variable,
set /p x=
then the batch file writes the variable to a file of some sort (abc.txt) Then later on, in a different batch file, the program retrieves the variable from the text document, and sets it as %x% again for whatever use. If there are any questions, or if I'm not clear enough, please comment, and I will revise. thanks.
In Batch Files, you can redirect input and ouput using < and > respectively.
Input.bat
#echo off
:: Take input and set value to x
set /p "x=: "
:: Print out the value of x to the screen, but redirect this to a text file
Echo %x% >> abc.txt
Echo EOF & Pause & Exit
Read.bat
#echo off
:: Set x to the first line in abc.txt
set /p x=< abc.txt
Echo First Line of abc.txt: %x%
Echo.
:: Set x to last line in abc.txt, incase it is multi-line file
for /f "delims=" %%a in (abc.txt) do (set x=%%a)
Echo Last Line of abc.txt: %x%
Echo.
Echo EOF & Pause & Exit
That should help you understand.
Mona.
I figured out a way that works best for me.
So I have the variable %x%, right? I got it from this:
set /p x=
then I write a mini-batch file.
echo set y=%x% >> abc.bat
then later on in a different script, I can use
call abc.bat
the variable y will be the value that I had in the origional batch script.

Resources