I have to do a few things in bash and batch. I'm stuck on the batch for-loop part. The instructions for part of my assignment are as follows:
::Doing a for loop from 1 to 100,
:: - Find the results of calculating each number from 1 to 100 mod 5.
:: - Append each number to the results.txt file
:: - After the for loop ends calculate and display the average of the results
:: - Append the average of the numbers to the results.txt file
::Code
:forLoop
echo.
::set /A temp-0
for /L %%x in (1, 1, 100) do (
set /A result="%%x %% 5"
call echo %%result%% >> results.txt
::%%temp+=%%result%%
)
::average=temp/100
::append average
GOTO exit
Other users helped me with result variable and the mod 5. However, I'm currently having trouble with temp. I think once I get temp to work I should be able to get the average part working without too much issue. My professor also mentioned that there are 3 different kinds of for-loops in batch, so I'm not even sure if I'm using the right one. Can anyone please help me figure this out.
echo.
set /A temp=0
for /L %%x in (1, 1, 100) do (
set /A result="%%x %% 5"
call echo %%result%%
CALL SET /a temp+=%%result%%
)
SET /a average=temp/100
ECHO %average% %temp%
This is quite straight-forward. Don't use :: comment-style within a block (parenthesised series of statements) as it's actually a broken-label which breaks the loop.
Beyond that, you need to call the set because you are not using delayedexpansion - hence the requirement to double the customary number of %s - same as call echo.
I've taken out the redirection so that the result simply appears on screen.
Related
I'm trying to set a variable every time + 1 with a batch file. So when the batch file opens it calls the file with the var and then redefines it plus 1. But when i open the file and then open count.bat i see this(in count.bat): set backupcount= instead of
set backupcount=1 which it should be (1 can also be 2, 3, 4, 5, enz).
This is the code i'm using:
#echo off
if exist "backup-tool\count.bat" call "backup-tool\count.bat"
if not exist "backup-tool\count.bat" echo set backupcount=0 > "backup-tool\count.bat"
call "backup-tool\count.bat"
if "%backupcount%"=="8" (
echo set backupcount=1 > "backup-tool\count.bat"
) else (
set /a "backupcount=backupcount+=1"
echo set backupcount=%backupcount% > "backup-tool\count.bat"
)
pause >nul
Anyone that knows what i'm doing wrong and tell me how i should do it?
All help is very much appreciated!
This one needs to use setlocal ENABLEDELAYEDEXPANSION, because you work in a block of code where you manipulate that variable and you want to use it right there(not the variable that is out of the block). This should work:
#echo off
setlocal ENABLEDELAYEDEXPANSION
if exist "backup-tool\count.bat" call "backup-tool\count.bat"
if not exist "backup-tool\count.bat" echo set backupcount=0 > "backup-tool\count.bat"
call "backup-tool\count.bat"
if "!backupcount!"=="8" (
echo set backupcount=1 > "backup-tool\count.bat"
) else (
set /a "backupcount=backupcount+=1"
echo set backupcount=!backupcount! > "backup-tool\count.bat"
)
pause >nul
Your original problem is related to Delayed Expansion as other answer said; however, your code is also unnecessarily complex. This is the way I would do it:
#echo off
if exist "backup-tool\count.bat" call "backup-tool\count.bat"
set /A "backupcount=backupcount%%8+1"
echo set "backupcount=%backupcount%" > "backup-tool\count.bat"
The set /A command takes as zero the value of any non-existent variable, so it is not necessary to initialize it with zero when the data file not exists.
If you want a repeating count from 1 to 8 and then reset the counter to 1, you may use the % Modulus operator in a simpler way that don't require an if. Type set /? for further details on %% operator, or see this Wikipedia article.
EDIT: Additional explanations added
The first time the program run the backupcount variable does not exist, so the set /A "backupcount=backupcount%%8+1" expression generate a 1 that is stored in the file. You may also add a set backupcount=0 command before the if just to avoid problems with previous executions of the same Batch file (or add a setlocal command at beginning).
The next time this variable is initialized with 1, so set /A "backupcount=backupcount%%8+1" expression produce a 2. The same happen with next numbers up to 8.
When the variable is initialized with 8 the expression backupcount%%8, that is the remainder when the variable is divided by 8, is zero; so the whole expression produce a 1 again.
For a long time I have wanted to program a game and I always thought it would be too difficult, so I started programming in the popular site Scratch (https://www.scratch.mit.edu) and doing websites with html and css.
I started to want to actually make a game with code so I am learning batch files. Unfortunately I only just started and am confused with a section on the game. I am making a game to try and recreate the game Swords and Sandals 2 to improve my knowledge of batch files. The important part of the game is the damage you deal to your opposition. Obviously you need a percentage chance for your attack to work to input luck into the game. I could easily do that but I want it so that if you have a higher level variable you will have a higher percentage chance, but if your opponent has some defence it will reduce the chance.
I did some basic maths to come up with some equations I was happy with, so here is the section of code :
if %move% equ 2 set /a tauntchance=%RANDOM%*(100+(%oppdefence%*5))/32768+1
if %move% equ 2 set /a tauntchance1=(7+(%charisma%*3))
if %move% equ 2 if tauntchance leq %tauntchance1% set /a damage= ((%charisma%*2)-%RANDOM%*1/32768+1)
if %move% equ 2 set /a energy=%energy%-%charisma%
move=The chosen option
tauntchance=A random number from 0-(100+(The opponents defence*5))
tauntchance1=(7+(Your charisma level*3))
3rd Line= If tauntchance is less than or equal to tauntchance1 then set your damage to Your Charisma Level*2 with a 50% chance to remove 1 or 0, which just stays the same.
4th Line= External variables including your energy amount.
When I run this code the window crashes. If you know if this is possible please let me know!
I have done lots of previous research without any results on this specific topic.
EDIT
With your answers it still just closes the window so I thought I would update the code from the problem onwards.
if %move% equ 2 set /a tauntchance=%RANDOM%*(100+(%oppdefence%*5)+1)/32768+1
if %move% equ 2 set /a tauntchance1=(7+(%charisma%*3))
if %move% equ 2 if %tauntchance% leq %tauntchance1% set /a damage=((%charisma%*2)-%RANDOM%*2/32768+1)
if %move% equ 2 set /a energy=%energy%-%charisma%
echo You have %energy%/%maxenergy% energy left.
echo.
echo You dealt %damage% damage.
echo.
pause >nul
Thank you for the responses anyway!
RESOLVED
I found the problem in some variables before in the code and have fixed it, thank you for the help.
set /a tauntchance=%RANDOM%*(100+(%oppdefence%*5))/32768+1
The value of %tauntchance% variable will never be equal to 0. To obtain the desirable result you should use this line
set /a tauntchance=%RANDOM%*(100+(%oppdefence%*5)+1)/32768
There is a problem with the 3rd line also. This part of the code
%random%*1/32768+1
will generate a sequence of 1's only, so the value of %damage% variable will always stay the same. To fix that use %random%*2/32768 instead.
Is that a complete code? Because you either have to initialise your variables first or better yet put them in quotation marks when you use them with if command. The code below runs fine, except that it is meaningless without the proper values of the variables.
#echo off
if "%move%" equ "2" set /a tauntchance=%RANDOM%*(100 + (%oppdefence%*5) + 1)/32768
if "%move%" equ "2" set /a tauntchance1=(7 + (%charisma%*3))
if "%move%" equ "2" if "%tauntchance%" leq "%tauntchance1%" set /a damage=((%charisma%*2) - %RANDOM% %% 2)
if "%move%" equ "2" set /a energy=%energy% - %charisma%
echo You have %energy%/%maxenergy% energy left.
echo.
echo You dealt %damage% damage.
echo.
pause > nul
I am currently working on a game in batch script and in one place, I need to make a multiplication of decimals. The problem is, the end result is always 0.
This is the code:
#echo off
echo Calcultating New Values
echo ...
ping localhost -n 2 >nul
set /p coal_price_buy_brt=<coal_price_buy_brt.wss
set /p coal_ind_buy=<coal_ind_buy.wss
cls
echo First Values :
echo ################################
echo ## Coal Price Brutto ## %coal_price_buy_brt% ##
echo ################################
echo ## Coal Index Buy ## %coal_ind_buy% ##
echo ################################
ping localhost -n 3 >nul
echo %coal_price_buy_brt%
echo %coal_ind_buy%
set ENABLEDELAYEDEXPANSION=coal_price_buy_net
set /p coal_price_buy_net=<calc %coal_price_buy_brt%*%coal_ind_buy%
echo Complete Table :
echo ################################
echo ## Coal Price Brutto ## %coal_price_buy_brt% ##
echo ################################
echo ## Coal Index Buy ## %coal_ind_buy% ##
echo ################################
echo ## Coal Price Netto ## %coal_price_buy_net% ##
echo ################################
The file data are:
coal_price_buy_brt = 150
coal_ind_buy = 0.84
EDIT :
4 years after this post, i'm now in IT Studies and realize that there is a difference between integers and floats in coding...
Thanks for having helped me back then !
The arithmetic operations of SET /A command can only manage integer numbers. Imagine you have a calculator that does NOT have the key for decimal point. How could you achieve this operation: 150*0.84? Well, if you know that the second value is always less than one with two decimals, you may execute 150*84 instead and insert a decimal point before the second digit (from right to left) of the result:
#echo off
set coal_price_buy_brt=150
set coal_ind_buy=0.84
rem Convert coal_ind_buy to integer
set coal_ind_buy=%coal_ind_buy:0.=%
rem Execute the multiplication
set /A result=coal_price_buy_brt*coal_ind_buy
echo Result as integer: %result%
echo Result as fixed point with two decimals: %result:~0,-2%.%result:~-2%
If the values may have integer part, then you may achieve the appropriate conversion to integer values, execute the multiplication, and insert the decimal point in the right place; however, you always must select a fixed number of decimal places ("fixed point arithmetic"), unless you want to convert the values to floating point (with an exponent of ten) and achieve all the apropriate conversions!
For further details about fixed point arithmetic operations in Batch, see: http://www.dostips.com/forum/viewtopic.php?f=3&t=2704&p=12523#p12523
I know this is an older question, but I have had a similar question come up with some scripting of my own. Perhaps my answer can still help someone out there with the same/similar question. My question to myself was, "How can use floating point decimal numbers in my batch script?" After much pondering and researching other personal questions on StackOverflow, I came up with the following example script. It pretty much converts a floating point number into a fraction in the form of two variables that can be used in the rest of your script. It can be used in tandem with this answer https://stackoverflow.com/a/20531384/2464491 to a similar question.
#echo off
setlocal EnableExtensions
setlocal EnableDelayedExpansion
REM This is how I do a block comment.
goto SOF
========Begin Comment========
Title: deciTest.bat
This batch script checks to see if the number inputed is an interger or a floating point number.
If it is a floating point number, it determines to how many decimal places up to 4096 places.
It then informes the user of how to use the floating point number in arithmatic equations.
Of course, if you include within your script, you can simply call upon the !intOut! and
!multiplier! variables elswhere in your script.
=========End Comment=========
:SOF
REM Check to see if the user supplied a number.
if "%1"=="" (
REM If not, tell them how to use the file.
echo Usage: deciTest.bat [number]
echo.
echo [number] The number to check. Enter either an integer
echo or a floating point number.
echo.
goto eof
)
REM Assign the user input to variable decNum
set decNum=%1
REM Plop the number into a file
echo !decNum!>decNum.tmp
REM Check to see if there is a decimal point
findstr /c:"." decNum.tmp >null
REM If it is found, the number is a floating point number
REM So lets make it so we can use it.
if %errorlevel%==0 (
REM Separate our Characteristic (before the .) and Mantissa (after the .)
for /f "tokens=1-18* delims=." %%a in (decNum.tmp) do (
REM Count the length of our Mantissa (How may decimal places?)
set "s=%%b"
set "s=!s!#"
set "decPlaces=0"
for %%P in (4096 2048 1024 512 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "decPlaces+=%%P"
set "s=!S:~%%P!"
)
)
REM Inform the user of our findings.
echo %%a.%%b is a floating point number with !decPlaces! decimal places
call :Integrate
echo.
REM Create the variable !intOUt! for use elswhere in the code
set /a intOut=%%a*!multiple!+%%b
REM Tell the user how to use this particular floating number
echo Your batch file can use !intOut! in your arithmatic equations.
echo Simply divide your result by !multiple!.
)
) else (
REM If it aint floatin', it's an integer
echo %1 is an integer
)
goto eof
:Integrate REM Create the !multiple! variable to be used elsewhere in the script
set count=!decPlaces!
set multiple=1
:startloop
set /a multiple*=10
set /a count-=1
if not !count!==0 goto startloop
:eof
The code demonstrates how to handle floating point numbers. Essentially, it turns floating point numbers into fractions (!intOut!/!multipler!). If you adjust your arithmetic a bit. Multiply by !intOut!, then send !intOut!/!multiplier! with however many decimal places you want to the example script found here: https://stackoverflow.com/a/20531384/2464491
I hope this helps anyone who has run into the same problem when trying to work with floating point numbers in a batch script. Sure it's not designed to work with such numbers, but you can always script your way around the problem.
You can call this batch file to do a mathematical evaluation.
Name it vbs.bat and then use call vbs 150*0.84 and the result will be in a variable called %val%
#echo off
>"%temp%\VBS.vbs" echo Set fso = CreateObject("Scripting.FileSystemObject") : Wscript.echo (%*)
for /f "delims=" %%a in ('cscript /nologo "%temp%\VBS.vbs"') do set "val=%%a"
del "%temp%\VBS.vbs"
Batch mathematics is INTEGER, hence 0.84 will either be interpreted as 0 or as an invalid number.
You may use an hybrid Batch-JScript file as described in this answer: looking for a way to calculate logarithm in a DOS batch file
This method allows you to evaluate any floating point operation, including logarithms, square roots, etc.
#if (#CodeSection == #Batch) #then
#echo off
rem Evaluate floating point expressions via JScript, for example:
call :Expr result=%coal_price_buy_brt%*%coal_ind_buy%
echo %result%
goto :EOF
:Expr result=expression
for /F "delims=" %%a in ('Cscript //nologo //E:JScript "%~F0" "%2"') do set "%1=%%a"
exit /B
#end
WScript.Echo(eval(WScript.Arguments.Unnamed.Item(0)));
For further details on available JScript mathemathic operations, see: http://msdn.microsoft.com/en-us/library/ie/b272f386(v=vs.94).aspx
I found this nice little tidbit of code here: https://stackoverflow.com/a/5262637/2128987
#echo off
set starttime=%TIME%
set startcsec=%STARTTIME:~9,2%
set startsecs=%STARTTIME:~6,2%
set startmins=%STARTTIME:~3,2%
set starthour=%STARTTIME:~0,2%
set /a starttime=(%starthour%*60*60*100)+(%startmins%*60*100)+(%startsecs%*100)+(%startcsec%)
:TimeThis
robocopy /e /NFL /NDL /NJH /NJS /nc /ns /np folder%rndfolder% %drvltr%:\f%dirnew%\
set endtime=%time%
set endcsec=%endTIME:~9,2%
set endsecs=%endTIME:~6,2%
set endmins=%endTIME:~3,2%
set endhour=%endTIME:~0,2%
if %endhour% LSS %starthour% set /a endhour+=24
set /a endtime=(%endhour%*60*60*100)+(%endmins%*60*100)+(%endsecs%*100)+(%endcsec%)
set /a timetaken= ( %endtime% - %starttime% )
set /a timetakens= %timetaken% / 100
set timetaken=%timetakens%.%timetaken:~-2%
echo.
echo Took: %timetaken% sec.
As a standalone program it works great. I am using it with a robocopy command basically to determine how long it takes to write a file.
I add one extra variable in it because I want to keep the raw seconds for calculation purposes. So I add the extra line set timeraw=%timetaken%:
set /a timetaken= ( %endtime% - %starttime% )
***set timeraw=%timetaken%***
set /a timetakens= %timetaken% / 100
set timetaken=%timetakens%.%timetaken:~-2%
My batch file also uses setlocal enabledelayedexpansion
Well sometimes it does not properly calculate the "starttime" or "endtime". It's keeps it as the raw time in 08:30:22.35 type format and results in the error:
Invalid number. Numeric constants are either decimal (17),hexadecima (0x11), or octal (021)
Well obviously because it contains non-numeric characters like the : character.
My batch file goes in a continuous loop forever as I am using it to read, write, delete files and folders for a specific torture test condition.
Any idea why it would intermittently not calculate the starttime or endtime variables?
edit:
I made some changes to my overall script. I no longer need enabledelayedexpansion, cleaned up some if then statements, and simplified code a little. But I still occasionally get it where the starttime or endtime variables remain as the raw time format of HH:MM:SS.CS and causes error in calculation.
Old question, but there are probably blocks of parentheses and when you change a variable within parentheses then you need to use delayed expansion.
Run this and examine the differences.
#echo off
set a=nothing
if z==z (
set a=b
echo %a%
)
pause
setlocal enabledelayedexpansion
set a=nothing
if z==z (
set a=b
echo !a!
)
pause
Gee - a question nearly a year old, with no answer.
I'll assume that the problem has now been solved, so as a matter of record, I'd conclude that the
"sometimes it does not properly calculate" is because the hour/minute/second/hundredths will contain "08" or "09" which are not octal numbers.
The solution is
set /a startcsec=1%STARTTIME:~9,2% - 100
and repeat with each of the other 3 start time-segments; then repeat again with the end parts.
In addition, it could be that the hour is being presented with 0s suppressed. In this case, I'd suggest
set starttime=0%TIME: =%
set starttime=%startTIME:~-11%
set /a startcsec=1%STARTTIME:~9,2% - 100
where the first line prefixes the time with a '0', and replaces Space with [nothing]
the second selects just the last 11 characters of the result
and the last is the familiar form, using the resultant hh:mm:ss.cc format.
(obviously, the remainder of the substring-and-calculate method needs also to be implemented)
How can I count how many calls of a cmd file?
I'm struggling with something like this but it didn't work:
#IF NOT EXIST Calls.log echo. > Calls.log
#for %%i in (Calls.log) do set size=%%~zi
#IF %size% EQU 0 (
#ECHO 1 > Calls.log
) ELSE (
#set /p v=<Calls.log
#set /A v+=1
#echo %v% > Calls.log
)
If all you're trying to do is count how many times a cmd script is called, you can just append one character to a file every time it runs rather than fiddling around with expression evaluation every time the script is run. This also has the advantage of making the script quicker since the analysis of the count is moved elsewhere.
The counter file expands by one byte every time, so watch out if you're calling it a truly large number of times since the file will expand. But even, calling it once per second, a 1G file will accrue only after 30 years.
At the top of your script, just put:
set >>countfile.txt <nul: /p x=X
This simply adds the character X to the end of the countfile.txt file every time the script is called. It uses the "set/p" command with input/output redirection, which is the only way I'm aware of to get a character out without a CR/LF following it (like the UNIX "echo -n").
To get a count of the number of calls to date, you can use the file size environment variable modifier, as in the following script (I expect this will be done less often than running the script so it's better to put the grunt work here [in fact, it's not a lot of grunt work since it's not counting the characters, rather it gets the information directly from the directory entry]):
#echo off
goto :main
:getsize
echo %~z1
goto :eof
:main
setlocal enableextensions enabledelayedexpansion
call :getsize countfile.txt
endlocal
To reset the count, use the following extremely complicated command (I'm thinking of patenting this):
del countfile.txt
One other thing I'd suggest - you don't need to prefix every command with "#" to prevent echo, you can simply put "#echo off" at the top of your script for global no-echo. If you want to selectively echo some commands, just ignore this paragraph.
The complete if block is parsed at once and thus all environment variables in it are getting replaced by their values at the time before the if block gets executes. You need to enable delayed variable expansion and use !v!:
#setlocal enabledelayedexpansion
#IF NOT EXIST Calls.log echo. > Calls.log
#for %%i in (Calls.log) do set size=%%~zi
#IF %size% EQU 0 (
#ECHO 1 > Calls.log
) ELSE (
#set /p v=<Calls.log
#set /A v+=1
#echo !v! > Calls.log
)
And you can simplify the code as follows:
#echo off
setlocal enabledelayedexpansion
IF NOT EXIST Calls.log (
ECHO 1 > Calls.log
) ELSE (
set /p v=<Calls.log
set /A v+=1
echo !v! > Calls.log
)
There is no need to create the file beforehand (and even that I'd solve with copy nul Calls.log, since that ensures a file size of 0).
The following code works on my computer:
#if not exist Calls.log (
echo 0 > Calls.log
)
#set /p v=< Calls.log
#set /a v=v+1
echo %v% > Calls.log