ECHO Command with variable - batch-file

I'm new to batch and I'm trying to make a speed/distance/time calculator. The code works fine until I try to echo the total. Here's my code:
#ECHO off
COLOR 0f
TITLE Speed Distance Time Calculator
:BEGIN
SET /P type="Calculate speed/distance/time? (S/D/T): "
CLS
IF /I "%type%"=="s" (
SET /P distance="Distance: "
CLS
SET /P dUnits="Distance units (mile/m/km):"
CLS
SET /P time="Time: "
CLS
SET /P tUnits="Time units (h/s):"
CLS
SET total=%distance%/%time%
ECHO %total%
)
It outputs:
ECHO is off
I've looked around for answers and have tried "enabledelayedexpansion" but it did not work.

To do division (or any arithmetic operation) in a batch file using the SET command, you have to specify the /A switch. Additionally, you'll need to turn on delayed variable expansion, since you will be dynamically changing variables in the batch file, and then using them.
When using delayed expansion variables you must reference them with ! instead of %. The exclamation marks tell the command processor that you want that particular variable's expansion to be delayed. Any variables that use percentage signs will be expanded at initial parse time.
So at the top of your batch file, under the #ECHO off, turn on delayed expansion:
SETLOCAL EnableDelayedExpansion
Then perform the calculation like so:
SET /A total=!distance!/!time!
ECHO !total!

Related

Batch - Multiple file outputting with variable replace value

I'm trying to create a .bat program to replace two strings inside one text file and output the modified text multiple times.
So far so good...
The purpose of my program is to calculate the number of months between two dates (Ex: 01/2016 and 05/2017 will result in 17 months), and generate one configuration file for each month for a 3rd party program (17 output files in my example). This can be accomplished by replacing two tags ( and ) inside a template configuration file with the respective month/year values in that range.
My code so far is below:
#echo off &setlocal
setlocal ENABLEDELAYEDEXPANSION
cls
set "CNST_SEARCH_YEAR=<VAR_YEAR>"
set "CNST_SEARCH_MONTH=<VAR_MONTH>"
set "CNST_FILE_TEMPLATE=config_template.properties"
set "CNST_FILE_TMP=tmp_config.properties"
rem ===============================
rem INPUT DO USUÁRIO
rem ===============================
set "start_year=2014"
set "start_month=3"
set "end_year=2015"
set "end_month=7"
rem ===============================
rem DEFINIÇÂO DO TOTAL DE ITERAÇÕES
rem ===============================
set /a "iterations=(%end_year%*12 + %end_month%) - (%start_year%*12 + %start_month%) + 1"
echo DISPARO AUTOMATICO DA ROTINA AGENT - v1.0
echo ================================
echo Total de iteracoes: %iterations%
echo ================================
rem ===============================
rem EXECUÇÃO DO LOOP PRINCIPAL
rem ===============================
set v_year=%start_year%
set v_month=%start_month%
for /L %%i IN (1, 1, %iterations%) do (
echo ================================
echo Iteracao: %%i
echo !v_year! / !v_month!
echo Gerando parametrizacoes...
for /f "delims=" %%j in (%CNST_FILE_TEMPLATE%) do (
set "line=%%j"
set "line=!line:%CNST_SEARCH_YEAR%=!v_year!"
set "line=!line:%CNST_SEARCH_MONTH%=!v_month!"
echo !line! >> "%CNST_FILE_TMP%_%%i"
)
echo Executando Agent...
rem jre\bin\java.exe -jar gdc-agent-totvs-2.0.0.jar %CNST_FILE_TMP%
echo Apagando arquivo temporario...
rem del %CNST_FILE_TMP%
IF !v_month! EQU 12 (
set v_month=1
set /a v_year=!v_year!+1
) ELSE (
set /a v_month=!v_month!+1
)
echo ================================
)
endlocal
pause
My problem relies in the lines:
set "line=!line:%CNST_SEARCH_YEAR%=v_year!"
set "line=!line:%CNST_SEARCH_MONTH%=v_month!"
Because I can't use delayedExpansion multiple times inside that command. Also I can't define the v_year and v_month variables before the for loop, because their values are being set by the loop.
I'm using plain batch script since this program will be sent to other people who might not have powershell or other scripting tool.
Any ideas people?
Thanks.
Or combine the old fashioned call variant
call set "line=%%line:!CNST_SEARCH_YEAR!=!v_year!%%"
call set "line=%%line:!CNST_SEARCH_MONTH!=!v_month!%%"
To escape a percent sign from being interpreted as enclosing a variable you have to double it. The parser reduces the two %% to a single one in this step.
The normal delayed expansion for the !var! is executed.
The call forces a second evaluation of the parser which find this time the single percent signs and acts on current values.
To learn more on this topic read How does the Windows Command Interpreter (CMD.EXE) parse scripts?
You can try with something like
for %%v in (!v_year!) do set "line=!line:%CNST_SEARCH_YEAR%=%%v!"
This simply moves the delayed expanded value into a for replaceable parameter that can be used in the delayed expansion expression used in the set command

Echo &variable% in a loop is delayed by 1 count. Echo is off error

Im trying to make a CMD batch script that will do the following.
Read the first line of a text file. The first line of the text file contains a date.
Delete the text file if the date is 3 months old from current date.
For illustration,
the first line of file A is Hello1, the first line of file B is Hello2
I want to get an output showing this
%counter% %first line of text file%,
so for my example it should look like this:
2 Hello2
1 Hello1
but instead, i am getting this:
2
1 Hello2
My current code is this.
set file.1=A.txt
set file.2=B.txt
set counter=2
SETLOCAL EnableDelayedExpansion
set counter=%counter%
:loop
if %counter% NEQ 0 (
set /p texte=<!file.%counter%!
echo %counter% %texte%
set /a counter=%counter%-1
gotop loop)
How do I fix this?
You have set it up for delayed expansion with your setlocal command (which should probably have a corresponding endlocal by the way) but you don't appear to be using delayed expansion in all the places it's needed.
Delayed expansion of variables requires the use of ! for expansion, not %.
Of course, once you do that, you're going to find issues with an expression like !file.!counter!! because cmd.exe is not the, err, greatest tool in the world :-)
However, that fact has produced some of the sneakiest coders in the world by forcing them to work around such limitations and you can do double-indirection of variables by using call as per the following program:
#setlocal enableextensions enabledelayedexpansion
#echo off
set file.1=A.txt
set file.2=B.txt
set counter=2
:loop
if !counter! NEQ 0 (
call :sneaky_set fspec file.!counter!
set /p texte=<!fspec!
echo !counter! !texte!
set /a counter=!counter!-1
goto loop
)
endlocal
goto :eof
:sneaky_set
set %1=!%2!
goto :eof
The call statement there passesfspec and file.N (the first level of indirection and where N is the current value in counter) to sneaky_set. It in turn executes:
set fspec=!file.N!
which is the second level of indirection and therefore sets file to the correct *.txt value.

Clear variables values in batch

I am reading two text files to set the values of two variables (u,l). Now I want to write script to run multiple files. When it is reading first file it will set the variables from the respective files but when it is reading second file it will set the same values of those variables.
#echo on
set /p u=< ul.txt
set /p l=< ll.txt
echo %u%-%l%
I tried SETLOCAL/ENDLOCAL option but in that case it is not reading variables values and getting error that ECHO is off. Even I wrote set u= and set l= at the initial of the script but not working in my case.
Your code, as given, works fine. However, I'm guessing it is code from inside an if statement, or for loop. If that is the case, you should use delayed expansion. You can use delayded expansion like this:
This is an example, not the exact code you need:
#echo on
setlocal EnableDelayedExpansion
if 1 equ 1 (
set /p "u=< ul.txt"
set /p "l=< ll.txt"
echo !u!-!l!
)
pause
FOR /L %%G IN (1,1,1) DO (
set /p "u=< ul.txt"
set /p "l=< ll.txt"
echo !u!-!l!
)
pause
set /p u=< ul.txt
set /p l=< ll.txt
echo %u%-%l%
pause
Note that inside the if statement and for loop, you replace % signs, when they are around variable names, with !. So %someVar% becomes !someVar!, but %%F stays %%F.
Outside of if statements and for loops, so outside of (), you use the normal %someVar%

How to use the variable inside another variable

How would I do to use the variable %number% in this case?
Set Test=%strToMeasure:~-%Number%%
Whenever I use a variable the result comes out like this:
No variable only the number:
Set Test=%strToMeasure:~-3%
With variable:
Set Test=%strToMeasure:~-%Number%%
Full Code
#echo off
Set "strToMeasure=This is a string"
call :strLen strToMeasure strlen
echo.String is %strlen% characters long
Set /A number = %strlen% - 13
Set Test=%strToMeasure:~-%Number%%
Echo %strToMeasure%
Echo %Test%
pause
exit /b
:strLen
setlocal enabledelayedexpansion
:strLen_Loop
if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
(endlocal & set %2=%len%)
goto :eof
You have two options in order to correctly execute this line:
Set Test=%strToMeasure:~-%Number%%
1- Doubling the percent signs for the second expansion and using call command:
Call Set Test=%%strToMeasure:~-%Number%%%
2- Using Delayed Expansion and enclosing the second expansion in exclamation marks:
setlocal EnableDelayedExpansion
. . .
Set Test=!strToMeasure:~-%Number%!
The second method is the usual way to solve this problem and it run faster than the former. You may review further details of this behavior at this post.

Setting up variable in for loop in batch

I am fighting with little piece of code for last two days.
In this I am not able to set variable in a for loop.
I want to assign a filename to a variable for string manipulation.
echo off
for /f %%a IN ('dir /b *_ah.ttf') DO (
set /a fName=%%~na
echo %fName%
)
When I echo fName variable I get only last filename repeatedly number of times for for loop count.
(I want to pass this variable as an argument to some batch file as follows
ttfhnt --strong-stem-width=D -i %%a %fName:~0,-3%.ttf
but its failing due to above problem)
Can somebody help me please?
When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.
This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.
To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.
And set /A is only used for arithmetic operations
setlocal enabledelayedexpansion
for /f "delims=" %%a IN ('dir /b *_ah.ttf') DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
edited to adapt to comments
While for command is able to execute a command (in the OP code, the dir...), retrieve its output and then iterate over the lines in this output, the original reason for the command is to iterate over a set of files. In this form, the code can be written as
setlocal enabledelayedexpansion
for %%a IN ("*_ah.ttf") DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
Now, the for command replaceable parameter will iterate over the indicated set of files. (execute for /? for a list of all the command options).
But as foxidrive points, the problem with delayed expansion are the exclamation signs. Without delayed expansion, they are another normal character, but with delayed expansion they frequently become a problem when a value containig them is assigned/echoed.
A quick test
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
echo normal : %test%
for /f "delims=" %%a in ("!test!") do echo for : %%a
Will show
---------------------
test=this is a test!
---------------------
delayed : this is a test!
normal : this is a test
for : this is a test
Obviously when the value is a file name, this behaviour will make the code find or not the file.
Depending on the case different solutions can be used, but usually it involves the activation / desactivation of the delayed expansion behaviour (beware, the endlocal removes any change in environment variables from the previous setlocal).
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
rem Commuted to no delayed expansion
setlocal disabledelayedexpansion
echo normal : %test%
endlocal
rem Cancelled the initial enable delayed expansion
for /f "delims=" %%a in ("!test!") do endlocal & echo for : %%a
rem The last endlocal has removed the changes to the variable
echo no data : [%test%]

Resources