Variable multiplication in batch file - batch-file

I am trying to multiply two variables through a batch file. The code is:
SETLOCAL ENABLEDELAYEDEXPANSION
#ECHO OFF
SET /A res = 0
FOR /L %%i IN (1,1,2) DO (
FOR /L %%j IN (1,1,3) DO (
SET /A res = %%i * %%j
ECHO Multiplying %%i and %%j
ECHO %res%
)
)
The problem is that I always get 0 as a result. Can anyone please tell me what I am doing wrong?
Here's the output:
Multiplying 1 and 1
0
Multiplying 1 and 2
0
Multiplying 1 and 3
0
Multiplying 2 and 1
0
Multiplying 2 and 2
0
Multiplying 2 and 3
0
Thank you!

This is because you're outputting the result in the same block where you set it. Read up help set on delayed expansion. cmd expands %variables% when a command is parsed which is much earlier than when it's executed. As a side effect your %res% is replaced by zero.
You need
setlocal enabledelayedexpansion
at the start and then use !res! instead of %res%.
Side note: Ditch the spaces around the = in your set calls. They work as you expect when using them with /A but won't otherwise.

You need to use ! to expand the variable due to your delayed expansion setting:
SETLOCAL ENABLEDELAYEDEXPANSION
#ECHO OFF
SET res = 0
FOR /L %%i IN (1,1,2) DO (
FOR /L %%j IN (1,1,3) DO (
SET /A res = %%i * %%j
ECHO Multiplying %%i and %%j
ECHO !res!
)
)

Related

How to find and replace a number in a file and increment it?

I have 5000 same files and I need to update numeric value in its content and increment it. Below is the batch script I use to find and replace a number in a certain file called BULK_1.txt.
I am not sure on how to increment the value after running search and replace.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set search=01118596270001
set replace=01118596270002
set "textFile=BULK_1.txt"
set "rootDir=C:\Batch"
for %%j in ("%rootDir%\%textFile%") do (
for /f "delims=" %%i in ('type "%%~j" ^& break ^> "%%~j"') do (
set "line=%%i"
setlocal EnableDelayedExpansion
set "line=!line:%search%=%replace%!"
>>"%%~j" echo(!line!
endlocal
)
)
endlocal
The result should be like below. The last 4 digits should be updated from 0001 to 5000 for each file
Content of the BULK_1.txt:
DMAIN Test_data 01118596270001
DDOC_DATA Test_docdata 01118596270001
Content of the BULK_2:
DMAIN Test_data 01118596270002
DDOC_DATA Test_docdata 01118596270002
Content of the BULK_3:
DMAIN Test_data 01118596270003
DDOC_DATA Test_docdata 01118596270003
According to your requirements you may need:
#echo off
setlocal EnableDelayedExpansion
for /L %%A IN (1 1 5000) do (
set num_processed=%%A
call:find_len num_processed
if !len! EQU 1 (set num_%%A=0111859627000%%A)
if !len! EQU 2 (set num_%%A=011185962700%%A)
if !len! EQU 3 (set num_%%A=01118596270%%A)
if !len! EQU 4 (set num_%%A=0111859627%%A)
)
for /L %%A IN (1 1 5000) do (
for /L %%B IN (1 1 2) do (
if %%B EQU 1 (
echo DMAIN Test_data !num_%%A!>BULK_%%A.txt
) else (
echo DDOC_DATA Test_docdata !num_%%A!>>BULK_%%A.txt
)
)
)
:find_len
set "s=!%~1!#"
set "len=0"
for %%P in (4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
My code uses the way that #jeb has suggested here slightly edited.
First, we make a loop to count from 1 to 5000.
We want to count the length of each of these numbers. calling the find_len subroutine does this.
If the string length of the variable is 1, then it must be 0001. The number in front is the same in all cases.
If the string length of the variable is 2, then it must be 0010. The number in front is the same in all cases.
If the string length of the variable is 3, then it must be 0100. The number in front is the same in all cases.
If the string length of the variable is 4, then it must be 1000. The number in front is the same in all cases.
Note: If we tried something similar to set /a 0000+1 the result would be 1. That is why all of these complicates!.
In all cases, the variable names will be num_numberCurrentlyProcessed.
Now another loop, actually the same as before. It will loop 5000 times and will create 5000 files in the format BULK_num.txt.
Inside this loop, another loop is required from 1 to 2, as each file must have 2 lines.
If we are in line 1, we echo the specific text specified by OP.
If we are in line 1, we echo again the specific text specified by OP.
"I have 5000 same files" - it's a lot faster to write them from scratch than to edit each of them:
#echo off
setlocal enabledelayedexpansion
set "Hdr=DMAIN Test_data 0111859627"
set "Dta=DDOC_DATA Test_docdata 0111859627"
for /l %%a in (1,1,5000) do (
set "num=0000%%a" REM prepend some zeros
set "num=!num:~-4!" REM take last four chars
>Bulk_%%a.txt (
echo %Hdr%!num!
echo %Dta%!num!
)
)

Compare the absolute value of a number in a for loop to determine if it's to be used?

I'm now on the bigger problem I mentioned in this post...Searching a text file and sending only numbers greater than a certain absolute value to text file?
I figured if I set num=!num:-=! with an original value of, say, -17, then the !errorlevel! will evaluate to true or 0 right? Something's not working here though...
To clarify, I need to filter out only the first and fourth tokens of lines in which the fourth token is either greater than 3 or less than -3, as well as any lines that do not have a 4th token (this part is solved). I have tried using the /A option of set and it doesnt seem to work still.
setlocal enabledelayedexpansion
set "min=-"
for /f "tokens=1,4" %%a in ('findstr /b /r /c:"[^ ]*:S:" print.log') do (
if %%b=="" (echo %%a ^*^*^* >>new.txt) else (
set num="%%b"
set num=!num:-=!
if !errorlevel!==0 (
if !num! GTR 3 echo %%a !min!!num! >> new.txt
) else (
if !num! GTR 3 echo %%a !num! >> new.txt
)
)
)
exit /b
The text in print.log looks like:
ksdf 0 0 -4
as7d:S:asf 0 0 -4
kc:S:cd3 0 0 -2
asdk:S:s 0 0 6
lasd:S:dd 0 0
#echo off
setlocal enableextensions disabledelayedexpansion
>"new.txt" (
for /f "tokens=1,4" %%a in ('
findstr /b /r /c:"[^ ]*:S:" print.log
') do if "%%~b"=="" (echo %%a ***) else (
set "print=1"
if %%b lss 4 if %%b gtr -4 set "print="
if defined print echo %%a %%b
)
)
Instead of printing when the value is lower than -3 OR greater than 3, it does not print when the value is lower than 4 AND greater than -4
-6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6
^.................^
don't print
#ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
set "threshold=3"
>new.txt (
for /f "tokens=1,4" %%a in ('findstr /b /r /c:"[^ ]*:S:" print.log') do (
if "%%b"=="" (
echo %%a ^*^*^*
) else (
set /A num=%%b
if !num! GEQ 0 (
set "min="
) else (
set "num=!num:-=!"
set "min=-"
)
if !num! GTR %threshold% echo %%a !min!!num!
)
)
)
set /A num=%%b
set num=!num:-=!
This establishes num as the arithmetic value of %%b. Note that environment variables are always strings. The assignment does the conversion each way as required. Your assignment would have assigned a value of (eg) "-4" (including the quotes)
The second command uses the string in num and changes all - to *nothing* hence calculating the absolte-value assuming it's of the appropriate -number structure.
Beyond that, I've no idea what your intentions are to create your output, since you;ve not indicated what the output should be. Either way, errorlevel won't be affected by theset AFAIAA.
Note that %%b will still contain the original number read at the point you are using echo. You can perform an if on %%b as well as on !num! - and use %%b or !num! in your echo as required.
You don't say what your gtr 3 business is designed to do...
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q42639932.txt"
FOR /f "tokens=1,4" %%a IN ('findstr /b /r /c:"[^ ]*:S:" "%filename1%"') DO (
if "%%b"=="" (echo %%a ^*^*^*) else (
set num=%%b
set num=!num:-=!
if !num! GTR 3 (echo %%a %%b
) else (
ECHO just FOR demo %%a %%b
)
)
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I used a file named q42639932.txt containing your data + extras for my testing.
For each line that passes the findstr filter, if column4 is missing, produce asterisks else set num to the value in %%b, and remove any - from it. If the resultant number is >3, echo the original %%b otherwise - well, I've produced a report line for completeness. Obviously, that can be remmed out.

variable as tokens in for loop

Im trying to make a batch file that loops thru an array containing numbers like this: 1 2 3 4 5.
In the first itteration of the loop I like to pick token 1 and 2. In the second 2 and 3, in the third 3 and 4 and so on.
I do think I should use ! in the variables first and second that I use as tokens. Like in the first FOR /F, but when I do, I get: !first!" was not expected here.
And if I use %, it does not count up.
Everything works except the variable tokens. Any one knowes how to? Any help or suggestions greatly appriciated.
This is the part Im struggeling with:
setlocal EnableDelayedExpansion
set first=1
set second=2
set N=4
set output="1 2 3 4 5"
set output=%output:"=%
for /L %%a in (1,1,%N%) do (
if !counter! equ active (
set /a first+=1
set /a second+=1
)
FOR /F "tokens=!first!" %%a IN ("%output%") DO (
set nr1=%%a
)
FOR /F "tokens=%second%" %%a IN ("%output%") DO (
set nr2=%%a
)
echo nr1 var: !nr1!
echo nr2 var: !nr2!
echo counter f: !first!
echo counter s: !second!
set counter=active
)
You cannot use delayed expanded variables in the options string of for /F. Neither can you use other for variables for that. But you can use normally (immediately) expanded variables. Also you can use argument references like %1, for example.
So a nice work-around for your problem is to place the for /F loop in a sub-routine and use call in the main program with the delayed expanded variables as arguments, like this:
#echo off
setlocal EnableDelayedExpansion
set /A first=1
set /A second=2
set /A N=4
set "output=1 2 3 4 5"
set "counter="
for /L %%a in (1,1,%N%) do (
if defined counter (
set /A first+=1
set /A second+=1
)
call :SUB !first! !second!
echo nr1 var: !nr1!
echo nr2 var: !nr2!
echo counter f: !first!
echo counter s: !second!
set "counter=active"
)
endlocal
exit /B
:SUB val_token1 val_token2
for /F "tokens=%~1,%~2" %%a in ("%output%") do (
if %~1 LSS %~2 (
set "nr1=%%a"
set "nr2=%%b"
) else if %~1 GTR %~2 (
set "nr1=%%b"
set "nr2=%%a"
) else (
set "nr1=%%a"
set "nr2=%%a"
)
)
exit /B
Since you are extracting tokens from the same string, I combined your two for /F loops into a single one. The if block in the for /F loop in the sub-routine :SUB is there just in case the second token number is not always greater than the first one. But if that can guaranteed, the for /F loop needs to contain only set "nr1=%%a" and set "nr2=%%b".

Batch file multiply positive variables return a negative number

I've been working on a Batch polygon area calculator and I got a problem.
I need to multiply 2 variables, but sometimes it return a negative number if the two positive variables are large.
Here's an example: 999999*999999 returns -729379967.
Code goes below:
REM Calc square area
:PolySqu
Cls
Echo Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Set /P "InputPolygonCalSqu=Enter one of the line's length in cm :"
Set /A SquArea=InputPolygonCalSqu * InputPolygonCalSqu
Cls
Echo Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Echo The area of this square is %SquArea% cm2.
Pause
Goto :PolygonCal
It seemed the command
Set /A SquArea="InputPolygonCalSqu * InputPolygonCalSqu
doesn't calculate properly.
As others already pointed out, a batch-file natively supports 32-bit signed integer arithmetics only.
The following code constitutes a work-around for multiplying non-negative numbers greater than the limit of 232 − 1 = 2147483647, using pure batch-file commands (let us call it multiply.bat):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define arguments here:
set "NUM1=%~1"
set "NUM2=%~2"
set "NUM3=%~3"
set "NUM4=%~4"
if defined NUM1 set "NUM1=%NUM1:"=""%
if defined NUM2 set "NUM2=%NUM2:"=""%
if defined NUM3 set "NUM3=%NUM3:"=%
call :VAL_ARGS NUM1 NUM2 NUM4 || exit /B 1
rem // Define constants here:
set /A "DIG=4" & set "PAD="
setlocal EnableDelayedExpansion
for /L %%J in (1,1,%DIG%) do set "PAD=!PAD!0"
endlocal & set "PAD=%PAD%"
rem // Determine string lengths:
call :STR_LEN LEN1 NUM1
call :STR_LEN LEN2 NUM2
set /A "LEN1=(LEN1-1)/DIG*DIG"
set /A "LEN2=(LEN2-1)/DIG*DIG"
set /A "LIM=LEN1+LEN2+DIG"
for /L %%I in (0,%DIG%,%LIM%) do set /A "RES[%%I]=0"
rem // Perform block-wise multiplication:
setlocal EnableDelayedExpansion
for /L %%J in (0,%DIG%,%LEN2%) do (
for /L %%I in (0,%DIG%,%LEN1%) do (
set /A "IDX=%%I+%%J"
if %%I EQU 0 (set "AUX1=-%DIG%") else (
set /A "AUX1=%DIG%+%%I" & set "AUX1=-!AUX1!,-%%I"
)
if %%J EQU 0 (set "AUX2=-%DIG%") else (
set /A "AUX2=%DIG%+%%J" & set "AUX2=-!AUX2!,-%%J"
)
for /F "tokens=1,2" %%M in ("!AUX1! !AUX2!") do (
set "AUX1=!NUM1:~%%M!" & set "AUX2=!NUM2:~%%N!"
)
call :NO_LEAD0 AUX1 !AUX1!
call :NO_LEAD0 AUX2 !AUX2!
set /A "RES[!IDX!]+=AUX1*AUX2"
set /A "NXT=IDX+DIG, DIT=DIG*2"
for /F "tokens=1,2,3" %%M in ("!IDX! !NXT! !DIT!") do (
set "AUX=!RES[%%M]:~-%%O,-%DIG%!"
set /A "RES[%%N]+=AUX"
set "RES[%%M]=!RES[%%M]:~-%DIG%!"
call :NO_LEAD0 RES[%%M] !RES[%%M]!
)
)
)
rem // Build resulting product:
set "RES=" & set "AUX="
for /L %%I in (0,%DIG%,%LIM%) do (
set /A "RES[%%I]+=AUX"
set /A "NXT=%%I+DIG"
for /L %%J in (!NXT!,%DIG%,!NXT!) do (
set "AUX=!RES[%%I]:~-%%J,-%DIG%!"
)
set "RES[%%I]=%PAD%!RES[%%I]!"
set "RES=!RES[%%I]:~-%DIG%!!RES!"
)
endlocal & set "RES=%RES%"
call :NO_LEAD0 RES %RES%
rem // Return resulting product:
echo(%RES%
if defined NUM3 (
endlocal
set "%NUM3%=%RES%"
) else (
endlocal
)
exit /B
:NO_LEAD0 rtn_var val_num
rem // Remove leading zeros from a number:
for /F "tokens=* delims=0" %%Z in ("%~2") do (
set "%~1=%%Z" & if not defined %~1 set "%~1=0"
)
exit /B 0
:STR_LEN rtn_length ref_string
rem // Retrieve length of string:
setlocal EnableDelayedExpansion
set "STR=!%~2!"
if not defined STR (set /A LEN=0) else (set /A LEN=1)
for %%L in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if defined STR (
set "INT=!STR:~%%L!"
if not "!INT!"=="" set /A LEN+=%%L & set "STR=!INT!"
)
)
endlocal & set "%~1=%LEN%"
exit /B 0
:VAL_ARGS ref_arg1 ref_arg2 ref_arg3
rem // Check arguments for validity:
if not defined %~1 >&2 echo ERROR: too few arguments given! & exit /B 1
if not defined %~2 >&2 echo ERROR: too few arguments given! & exit /B 1
if defined %~3 >&2 echo ERROR: too many arguments given! & exit /B 1
(call echo "%%%~1%%" | > nul findstr /R /C:"^\"[0-9][0-9]*\" $") || (
>&2 echo ERROR: argument 1 is not purely numeric! & exit /B 1
)
(call echo "%%%~2%%" | > nul findstr /R /C:"^\"[0-9][0-9]*\" $") || (
>&2 echo ERROR: argument 2 is not purely numeric! & exit /B 1
)
exit /B 0
To use it provide the two numbers to multiply as command line arguments; for instance:
multiply.bat 999999 999999
The resulting product is returned on the console:
999998000001
If you provide a third argument, the product is assigned to a variable with that name; for example:
multiply.bat 999999 999999 SquArea
This sets variable SquArea to the resulting value. The latter is also still returned on the console.
To silently assign the variable without any additional console output, redirect it to the nul device:
multiply.bat 999999 999999 SquArea > nul
Batch uses 32bit integers for storing numbers. This gives them a maximum size of 2^31 - 1 = 2,147,483,647.
999,999 * 999,999 = 999,998,000,001 which is larger than 2,147,483,647 therefore it "wraps" and starts from negatives.
This is a limitation of batch although there are some workarounds.
This might be useful
Can batch files not process large numbers?
The largest number batch can take is 2^31 - 1 = 2,147,483,647.
So again, it is starting back from negative and giving you that answer..
I noticed using pure batch, it would be somewhat to very difficult to implement operations that support the numbers beyond the 32-bit integer limit. So instead, I limited the numbers, so they won't overflow when multiplied.
REM Calc Square Area
:PolySqu
Cls
Echo Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Set /P "InputPolygonCalSqu=Enter one of the line's length in cm [Less then 40000] :"
If %InputPolygonCalSqu% GTR 40000 Goto :PolySqu
Set /A SquArea="InputPolygonCalSqu * InputPolygonCalSqu
Cls
Echo Polygon Area Calculator
For /L %%P In (1,1,57) Do Echo.
Echo The area of this square is %SquArea% cm2.
Pause
Goto :PolygonCal

Using an if statement inside a for loop

I am trying to make a batch file to solve the first Project Euler problem, http://projecteuler.net/problem=1, but I need an if statement inside my loop to check if n modulo 3 or 5 is 0. And the sum has suddenly stopped working.
My code:
echo off
set sum=0
for /l %%n in (1,1,999) do (
set a/ sum+=%%n *(only add if n%%3 == 0 or n%%5 == 0)*
)
echo %sum%
pause
Here is a very efficient solution, though it is a bit obfuscated:
#echo off
setlocal
set /a sum=0
for /l %%N in (1 1 999) do set /a "sum+=%%N/!((%%N%%5)*(%%N%%3))" 2>nul
echo %sum%
The expression (%%N%%5)*(%%N%%3) yields zero if %%N is divisible by 3 or 5, or non-zero if it is not divisible by either. The ! takes the inverse logical value, so 0 becomes 1, and non-zero becomes 0. Dividing %%N by that expression yields either %%N or a division by zero error. So simply add that entire expression to the sum, and redirect error messages to nul.
Final result - only numbers divisible by 3 or 5 are added :-)
#ECHO OFF
SETLOCAL
set /A sum=0
for /l %%n in (1,1,999) do (
CALL :modulo %%n
IF DEFINED addme set /a sum+=%%n
REM CALL echo %%n %%sum%% %%addme%%
)
echo %sum%
GOTO :EOF
:modulo
:: set addme if %1 %% 3 == 0 or %1 %% 5 == 0
SET /a addme = %1 %% 3
IF %addme%==0 GOTO :EOF
SET /a addme = %1 %% 5
IF %addme%==0 GOTO :EOF
SET "addme="
GOTO :eof
Simply pass each value to the :modulo routine in turn and set a flag value to (clear or not)
OR
#ECHO OFF
SETLOCAL enabledelayedexpansion
set /A sum=0
for /l %%n in (1,1,999) do (
SET /a mod=%%n %% 3
IF NOT !mod!==0 SET /a mod=%%n %% 5
IF !mod!== 0 set /a sum+=%%n
rem CALL echo %%n %%sum%%
)
echo %sum%
GOTO :EOF
which does the same thing using delayedexpansion.
And the sum has suddenly stopped working.
I think your sum stopped working because your set needs to have the slash in front of the 'a', and not behind it, like this:
SET /A sum+=%%n
Also, there isn't an OR operator in DOS Batch, so you'll need to use a nested IF for that. This worked for me:
echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set sum=0
for /l %%n in (1,1,999) do (
SET /A mod3=%%n%%3
SET /A mod5=%%n%%5
IF !mod3!==0 (
SET /A sum+=%%n
) ELSE (
IF !mod5!==0 (
SET /A sum+=%%n
)
)
)
echo %sum%
ENDLOCAL
If you need more help, check out Rob van der Woude's Scripting pages. Specifically, here is a link to his page on performing mathematical operations in DOS batch files.

Resources