How to add values to batch script variables? - batch-file

I know this may sound dumb, but I am a beginner at batch script, so I know almost nothing about it. Anyway, I want to change a variable; add 1 to it, here is my code:
#echo off
set num = 1
set num = %num% + 1
echo %num%
#echo on

Firstly, never use spaces in a standard set before and after =. This will create a variable with a trailing space and a value with a leading space. So technically you will have %num % and 1.
To demonstrate this, simply copy everything below and paste into a cmd prompt.
#echo off
set dummy = test
set dummy
echo %dummy%
echo %dummy %
#echo on
similarly with:
#echo off
set num = 1
set num | findstr /v "NUMBER"
set /a num+=1
set num | findstr /v "NUMBER"
echo %num%
echo %num %
#echo on
will result in two variables, %num % with a value of 1 and %num% with a value of 1
The results from the above will clarify your question in the comments.
To solve your arithmetic question, simply use the /a switch to specify that the values are numerical and we can use arithmetic sequence operators etc. on it.
#echo off
set "num=1"
set /a num+=1
echo %num%

Related

How to do a division with environment variables of a pseudo array within a FOR loop?

I have a constant variable X[0]=10 and a pseudo array with Y[0]=20 and Y[1]=30. N is defined with value 1.
I want to do an arithmetic operation using these environment variables in a for loop:
FOR /L %%i IN (0,1,%N%) DO (
echo !Y[%%i]!
set /a t[0]= !Y[%%i]! / !X[0]!
echo !t[0]!
echo/
)
But it doesn't work. There are displayed the messages:
Invalid number. Numbers are limited to 32-bit precision
ECHO command disabled.
What is the correct syntax?
(NOTE: this answer answered the question in its original form. The question then changed (see comments below))
Works fine for me after defining the used variables and enabling delayed expansion:
#echo off
setlocal enabledelayedexpansion
set N=1
set X[0]=10
set Y[0]=20
set Y[1]=30
FOR /L %%i IN (0,1,%N%) DO (
echo !Y[%%i]!
echo t[%%i]= !Y[%%i]! / !X[0]!
set /a t[%%i]= Y[%%i] / X[0]
echo !t[%%i]!
echo/
)
Output:
20
t[0]= 20 / 10
2
30
t[1]= 30 / 10
3
(Note: set /a can use variable names without enclosing them with % or !)

String Left & Mid Manipulation in a For Loop Batch File

I am writing a batch file with a for loop and am having trouble getting the left and mid logic working. I have successfully got this logic working outside of the for loop and have attached the code here.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
REM ZKR
set /a value = 1000
set /a range = "%value%"
set /a secrange = "%value%"
for /l %%x in (1, 1, 10) do (
echo %range%
set range=%range:~0,1%
set secrange=%secrange:~1,3%
echo !range!
echo !secrange!
set "newstr=!range!.!secrange!"
echo !newstr!
PAUSE
)
The output is shown here.
Split 1000
So the above cmd bat is able to split the string and then combine it with a period mark in the middle of the two split strings.
However, in my for loop I don't achieve this result. I know it may be a syntax issue but I am not sure how to fix it (I have tried a lot of different things already).
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
REM ZKR
set /a range = "X"
set /a secrange = "X"
for /l %%x in (1, 1, 10000) do (
if %%x geq 1000 (
if %%x lss 10000 (
set "range=%%x"
set "secrange=%%x"
echo !range!
echo !secrange!
set "range=%range%:~0,1%"
echo !range!
echo %range%
set secrange=%secrange:~1,3%
echo !secrange!
echo %secrange%
set "newstr=!range!.!secrange!"
echo !newstr!
PAUSE
)
REM End x greater 1000
)
REM End for loop
)
I use secrange the same way as I did in the original code without the for loop. I tried to manipulate the range portion with many different syntax combos but have not been able to output the left character 1. I only am able to output 0 for both characters.
I attached the output here. Bad Output
I understand there is an error (Echo is Off). Please ignore that it is just based off my echo output type for my personal testing.
The important two outputs are the third and fourth line (whichever one is referring to the correct syntax). Does anyone know how to fix the syntax in order to get the left character of 1000 in the for loop and the remaining 3 zeros of the 1000?

Calling an IF statement | Batch

Error Code:
If was unexpected at this time
The code sets the stats for each player then it calls the function to determine the amount of damage based on the stats and other variables
What I want to do is call a function with two IF statements within it
Example:
#echo off
set /a level=5
set /a opponentLevel=5
set /a movePower=50
set moveType=physical
goto player
:damageCalculator
if %moveType%==physical (
pause
set /a damage=(((( 2 * %level% / 5 + 2) * %attackStat% * %movePower% / %opponentDefenceStat%) / 50) + 2)
set /a opponentHealth-=%damage%
) else if %moveType%==special (
set /a damage=(((( 2 * %level% / 5 + 2) * %spAttackStat% * %movePower% / %opponentSpDefenceStat%) / 50) + 2)
set /a opponentHealth-=%damage%
)
goto :eof
:player
set type=fire
set/a health=19
set/a attackStat=10
set/a defenceStat=10
set/a spAttackStat=11
set/a spDefenceStat=10
set/a speedStat=12
:opponent
set /a opponentHealth=18
set /a opponentAttackStat=10
set /a opponentDefenceStat=9
set /a opponentSpAttackStat=8
set /a opponentSpDefenceStat=9
set /a opponentSpeedStat=13
:attack
pause
cls
call :damageCalculator
echo It did %damage% Damage!!
pause>nul
Is this just one of those things that batch can't do?
update per edited question
Your script has a few small issues. Firstly, I'll point out that it's often helpful to rem out #echo off when trying to track down the cause of a problem. Doing so in this case shows that the line causing your error is your first set /a line.
The reason your if statements are failing is because the parentheses within your math are being treated as the end of your code block. As soon as the closing parenthesis of ...5 + 2) is encountered, the batch interpreter treats that as the end of your if statement, and therefore gets confused when there's more stuff on the line. You need to quote your set /a statements to prevent this.
set /a "damage=(((( 2 * level / 5 + 2) * attackStat * movePower / opponentDefenceStat) / 50) + 2)"
See how I did set /a "variable=value" there? You could also escape the closing parentheses with a caret -- e.g. ^), but quoting the "var=value" is a little easier to read I think. The quotation marks keep the contents within the context of the command. They're basically saying, "This is a single token. It's a distinct part of the set command, not the code block as a whole." As a bonus, you can also see that the % signs aren't needed to retrieve variable values within a set /a command. Neat, huh?
You've got another problem. Since you're setting the %damage% variable within the same parenthetical code block as you're retrieving it, %damage% is going to be evaluated too early. You could setlocal enabledelayedexpansion and retrieve it as !damage!, and that would certainly work. But there's a simpler fix. Just put it outside the if statements. You're doing set /a opponentHealth-=damage regardless of whether the move type is physical or special, anyway, right?
:damageCalculator
if "%moveType%"=="physical" (
set /a "damage=(((( 2 * level / 5 + 2) * attackStat * movePower / opponentDefenceStat) / 50) + 2)"
) else if "%moveType%"=="special" (
set /a "damage=(((( 2 * level / 5 + 2) * spAttackStat * movePower / opponentSpDefenceStat) / 50) + 2)"
)
set /a opponentHealth-=damage
goto :eof
But still, you should include setlocal just below #echo off to keep from junking up your environment with variables that have no meaning outside the scope of this script.
Here's another tip. You can combine many statements within a single set /a line.
Before:
set /a level=5
set /a opponentLevel=5
set /a movePower=50
set "moveType=physical"
After:
set /a level=5, opponentLevel=5, movePower=50
set "moveType=physical"
original answer
Change your second if to else if or add a carriage return before it (after the preceding parenthesis). Also, move your goto :EOF to the next line after the closing parenthesis.
The explanation for this is that the cmd interpreter treats a parenthetical code block as a single command. So in essense,
if [true condition] (
action
) second command
is being evaluated as
if [true condition] (action) second command
which results in an error because there's no line break or other separator between the first command (the if) and the second. Here's an example of a valid compound command:
#echo off & setlocal
Compound command need:
unconditional AND (& -- right side always executes after the left)
example: endlocal & goto :EOF
logical AND (&& -- right side executes only if the preceding command exited zero)
example: find /i "text" "textfile.txt" >NUL && echo Text was found
pipe (| -- makes the command on the right do something with the output generated by the command on the left)
example: dir /s /b | more
logical OR (|| -- right side executes only if the preceding command exited non-zero)
example: tasklist | find /i "iexplore.exe" >NUL || echo Internet Explorer not running
... or in the case of an if statement, else.
if condition 1 (
action 1
) else if condition 2 (
action 2
) else (
action 3
)
Or if you want to check that two conditions are true:
if %var% leq 10 if %var% geq 5 (
echo Variable is between 5 and 10.
)
Of course, you don't have to use compound statements. There's no reason why a function can't have multiple if statements that aren't compound.
:fn <str>
if "%~1"=="fish" (echo Bloop.)
if "%~1"=="cats" (echo Meow.)
if "%~1"=="dogs" (echo Grrrr.)
goto :EOF
... is perfectly valid.

Batch: count the number of digits in a variable

I want to find a way to know the number of digits in variable. For now, I'm trying to use this code. In this example, %var% is the variable that I need to know the number of digits it has.
set x=1
set var=12345
:LOOP
set temp=%var:~0,%x%%
if %temp%==%var% goto END
set x=%x%+1
goto LOOP
:END
Theoretically, at the end of the code %x% would be the number of digits %var% has. However, it doesn't work. I found out the problem is at the 3rd line. I modified the code to diagnose:
set x=1
set var=12345
:LOOP
set temp=%var:~0,%x%%
echo %temp%
pause
if %temp%==%var% goto END
set x=%x%+1
goto LOOP
:END
The result echoed was:
x%%
Can anyone pinpoint my mistake or give an alternative solution to determine the number of digits in a variable?
Here's a short solution, that only works for numeric variables:
set /a Log=1%var:~1%-%var:~1% -0
set /a Len=%Log:0=+1%
The variable %Len% will contain the number of digits in %var%.
Explanation
The basic idea is to convert the first digit to 1, and the rest of them (the 'trailing' digits) to 0's. Then we can use the string replacement function to replace all the 0's with +1 giving us 1+1+1.... and evaluate the string as an arithmetic expression. This will give us the total number of digits.
The 'trailing' digits can be gotten using %var:~1% and we convert them to 0 by subtracting them from the variable itself: 45678 - 5678 gives 40000 etc. However, the above code subtracts them from 1%var:~1% instead, in order to replace the first digit with 1 (i.e. 1 followed by the 'trailing' digits).
The reason for the extra -0 is in case %var% only has one digit, for example 7. In that case, the expression 1%var:~1%-%var:~1% would evaluate to 1- and the shell would complain: Missing operand. The -0 ensures that we always have a valid expression.
Now that we've converted the variable in to the proper form into %Log%, we can replace every occurrence of 0 with +1using %Log:0=+1% and evaluate the resulting expression using set /a, giving us our final result.
The main problem with your code is
set temp=%var:~0,%x%%
This does not work. The parser is not able to properly determine what percent sign belongs to what variable. You can enable delayed expansion and write it as
set "temp=!var:~0,%x%!"
For alternative versions, to handle any length string, any of the posted answers will work.
For a simpler solution, if you are sure the string is under 10 characters, then this is an alternative
set "x=0123456789%var%"
set "x=%x:~-10,1%"
As there is no build in function for string length, you can write your own function.
#echo off
setlocal
set "myString=abcdef!%%^^()^!"
call :strlen result myString
echo %result%
goto :eof
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
set "s=!%~2!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~1=%len%"
exit /b
)
This function needs always 13 loops, instead of a simple strlen function which needs strlen-loops.
It handles all characters.
Source: How do you get the string length in a batch file?
Your are trying to do this loop :
#Echo Off
Set /P VrStr=Enter your string :
:Loop
If "%VrStr%" EQU "" Goto EndLoop
Set VrStr=%VrStr:~0,-1%
Set /A VrLgr+=1
Goto Loop
:EndLoop
Echo Number of char: %VrLgr%
Pause
You can use this to :
#echo off
setlocal EnableDelayedExpansion
Set /P $Tstring=Enter your string:
for /l %%a in (0,1,9000) do (set $t=!$Tstring:~%%a,1!&if not defined $t (echo [NB OF CHAR =] %%a&pause&exit /b))
pause

Floating point division in a batch file

I need to do a floating-point division in a dos batch.
I didn't find a way to do it. Something like this :
SET /A Res=10/3
returns a integer number.
Is it possible to do it ?
I know this is a very old topic, but I can't found a simple Batch method in all previous answers, so I post here a pure Batch solution that is very simple to use.
Perform operations using fixed point arithmetic in Batch is simple. "Fixed point" means that you must set a number of decimals in advance and keep it throughout the operations. Add and subtract operations between two Fixed Point numbers are performed directly. Multiply and division operations requires an auxiliary variable, that we can call "one", with the value of 1 with the right number of decimals (as "0" digits). After multiply, divide the product by "one"; before division, multiply the dividend by "one". Here it is:
#echo off
setlocal EnableDelayedExpansion
set decimals=2
set /A one=1, decimalsP1=decimals+1
for /L %%i in (1,1,%decimals%) do set "one=!one!0"
:getNumber
set /P "numA=Enter a number with %decimals% decimals: "
if "!numA:~-%decimalsP1%,1!" equ "." goto numOK
echo The number must have a point and %decimals% decimals
goto getNumber
:numOK
set numB=2.54
set "fpA=%numA:.=%"
set "fpB=%numB:.=%"
set /A add=fpA+fpB, sub=fpA-fpB, mul=fpA*fpB/one, div=fpA*one/fpB
echo %numA% + %numB% = !add:~0,-%decimals%!.!add:~-%decimals%!
echo %numA% - %numB% = !sub:~0,-%decimals%!.!sub:~-%decimals%!
echo %numA% * %numB% = !mul:~0,-%decimals%!.!mul:~-%decimals%!
echo %numA% / %numB% = !div:~0,-%decimals%!.!div:~-%decimals%!
For example:
Enter a number with 2 decimals: 3.76
3.76 + 2.54 = 6.30
3.76 - 2.54 = 1.22
3.76 * 2.54 = 9.55
3.76 / 2.54 = 1.48
Batch files as such do not support the floating point arithmetic. However, this article suggests a workaround that uses an external script file to do calculations. The script file should use some sort of eval function to evaluate the expression passed as an argument and return the result. Here's a sample VBScript file (eval.vbs) that does this:
WScript.Echo Eval(WScript.Arguments(0))
You can call this external script from your batch file, specify the expression to be evaluated and get the result back. For example:
#echo off
for /f %%n in ('cscript //nologo eval.vbs "10/3"') do (
set res=%%n
)
echo %res%
Of course, you'll get the result as a string, but it's better than nothing anyway, and you can pass the obtained result to the eval script as part of another expression.
According to this reference, there is no floating point type in DOS batch language:
Although variables do exist in the DOS batch programming language, they are extremely limited. There are no integer, pointer or floating point variable types, only strings.
I think what you are trying to do will be impossible without implementing your own division scheme to calculate the remainder explicitly.
I recently came across this batch file to compute an approximation of Pi.
There is a DivideByInteger label that might be useful to you: Stupid-Coding-Tricks-A-Batch-of-Pi
It uses a set of MaxQuadIndex variables, each containing a four-digit number (quadruple), in order to store the entire result. The code allows division by an integer between 1 and 10000, inclusive.
:DivideByInteger
if defined PiDebug echo.DivideByInteger %1 %2
set /a DBI_Carry = 0
for /L %%i in (!MaxQuadIndex!, -1, 0) do (
set /a DBI_Digit = DBI_Carry*10000 + %1_%%i
set /a DBI_Carry = DBI_Digit %% %2
set /a %1_%%i = DBI_Digit / %2
)
goto :EOF
A Print label is also available…
try this
SETLOCAL EnableExtensions EnableDelayedExpansion
call :calc_ 1 (99-(100*5/100^)^)
echo !calc_v!
goto :EOF
:calc_
set scale_=1
set calc_v=
for /l %%i in (1,1,%1) do set /a scale_*=10
set /a "calc_v=!scale_!*%2"
set /a calc_v1=!calc_v!/!scale_!
set /a calc_v2=!calc_v!-!calc_v1!*!scale_!
set calc_v=!calc_v1!.!calc_v2!
goto :EOF
just change
call :calc_ decimalpoint equataion
in the example
decimalpoint is 1
equataion is (99-(100*5/100^)^) ;make sure if you use () that you insert ^ before ) as in ^)
the answer is 94.0
if decimalpoint is 2
and equataion is 22/7 ;π pi
the answer is 3.14
I wrote a pure batch file specifically to do division. It takes the first number you input, and then divides it by the second one, and displays the result with as many decimal points as you specify.
Echo off
cls
if NOT "%3" == "" (
set n1=%1
set n2=%2
set max=%3
goto :begin
)
set counter=2
set n1=1
set n2=1
set ans=
:start
Echo.
Echo. 1 / 2
Echo.
Set /p N1= 1?
set /p N2= 2?
Set /p Max= Out how many Decimal Points?
:begin
set /a TmpAns=%N1%/%N2%
set ans=%TmpAns%.
:: Echo.%ans%.>Answer.txt
<nul set /p "=%Tmpans%."
set /a TmpSub=%N2%*%TmpAns%
set /a N1=%N1%-%TmpSub%
set N1=%N1%0
If NOT "%n1%" == "00" (
if %n1% LSS %N2% (
set N1=%N1%0
set ans=%ans%0
)
) else (
Goto :Finished
)
set count=0
:loop
If "%count%" == "%max%" (
Goto :Finished
)
set /a TmpAns=%N1%/%N2%
set ans=%ans%%TmpAns%
<nul set /p "=%Tmpans%"
set /a TmpSub=%N2%*%TmpAns%
set /a N1=%N1%-%TmpSub%
set N1=%N1%0
If NOT "%n1%" == "00" (
if %n1% LSS %N2% (
set N1=%N1%0
set ans=%ans%0
)
) else (
Goto :Finished
)
set /a count=%count%+1
goto :loop
:finished
cls
Echo.
Echo.
Echo.The Number
Echo.%ans%
Echo.
Echo.
set n1=1
set n2=1
pause
goto :eof
:eof
The answer put into the variable %Ans%. It can also be called with parameters. ("Divide.bat 50 27 5" would give you 50/27 out 5 decimal points.)
Since nowadays PowerShell is present on almost all machines, I would let PowerShell do the math and return the result to the batch.
Example:
set divident=10
set divisor=3
for /f "delims=" %%a in ('powershell -Command %divident%/%divisor%') do set result=%%a
#echo %result%
Explanation:
Input variables: Use set variables to define divident and divisor.
Calling powershell and assign result to a batch variable: for /f "delims=" %%a in ('powershell -Command ...) do set result=%%a (you may also check here: How to put a single PowerShell output string into a cmd variable?)
Note the above code will only work with integer input variables.
To support floating point input variables, we need to send the variables as strings inside quotations ("%variable%") and convert the strings within PowerShell back to Double, otherwise batch would interpret the commas as delimiters and PowerShell could not interpret the numbers.
Example:
set divident=10,5
set divisor=3,4
for /f "delims=" %%a in ('powershell -Command [convert]::ToDouble^(\"%divident%\"^)
/[convert]::ToDouble^(\"%divisor%\"^)') do set result=%%a
#echo %result%
Explanation:
Note in PowerShell you would do this like [convert]::ToDouble("10,5")/[convert]::ToDouble("3,5"). However in batch we need to escape the quotes using backslash, and we also need to add a "^" sign before and after the quoted parts: [convert]::ToDouble^("%divident%"^)/[convert]::ToDouble^("%divisor%"^)
If you're running in a command shell on Windows (rather than DOS), you can use VBScript to evaluate complex expressions including floating point math for you.
I have written a small helper library you can call to do this.
EvalBat Library on GitHub

Resources