So I've been wanting on doing something very simple and ran into this, which I don't understand why:
#echo off
setlocal enabledelayedexpansion
set /a var1=10
set /a var2=10
set /a var3=10
:test1
if %var1%==%var2% (
if %var2%==%var3% (
echo This Works
pause
)
)
:test2
if %var1%==%var2%==%var3% (
echo But this does not
pause
)
In this case, the test1 label works perfectly but the test2 label doesn't work.
Can anyone help me understanding why?
According to the help of the if command (type if /?), you can only compare two expressions but not three.
However, you could concatenate multiple comparisons:
if %var1% equ %var2% if %var2% equ %var3% (
rem // Do something...
)
This is a short form of the following (which becomes particularly relevant as soon as you want to use else clauses):
if %var1% equ %var2% (
if %var2% equ %var3% (
rem // Do something...
)
)
In the above I used the equ operator rather than == since you are comparing integers.
If you want to compare strings, use ==, together with quotation (to avoid issues with empty strings and to protect special characters):
if "%var1%"=="%var2%" if "%var2%"=="%var3%" (
rem // Do something...
)
No knowledge of batch, but I can share context from some other programming languages where this typically doesn't work.
This will likely parse as either:
(%var1% == %var2%) == %var3%, or
%var1% == (%var2% == %var3%).
In either case, one of the equalities between two variables is evaluated first, resulting in a false or true that will probably not be equal to the third variable (even if it does happen to, that's probably not what you want).
The solution is to use two seperate equalities, conjuncted with an AND operator, like %var1%==%var2% AND %var2%==%var3%
Related
Hi I'm new in Batch File and still learning.
I would like to ask about Symbol Equivalent in Batch File.
I have a batch file to compare year.
IF %YEAR% LEQ 2017 (
echo true
) else (
echo false
)
However, it keeps returning false value, even when the values are match or values are mismatch.
Am I wrong put the symbol in Batch File?
Thank you in advance for your help!
The '%year%' variable seem like you want to get the year number, also you just new to batch file. So I want to show you the '%date%' variable.
If you only type echo %date% , you'll see the text like: MM/DD/YYYY
The problem is, you only need the year number. Now you have to use like this:
set year=%date:~6,4%
IF year% LEQ 2017 (
echo true
) else (
echo false
)
set year=%date:~6,4% mean you only get the number from char #6 (front of year number '2') and from char #6, you only get 4 more char (from #6 to end of string)
Hope this helpful, and remember, there is no %year% variable.
So I have this program I'm trying to make:
//#echo off
#title Calculate Caloric Needs
set /p name=What is the name of the individual in question?
CLS
:START
set /p sex=Is %name% a M(ale) or F(emale)? (M/F)
CLS
if NOT %sex%==M if NOT %sex%==F (
echo Invalid Selection...Try Again....
goto START
)
set /p w=What is the target weight of %name%?
CLS
set /p h=What is the height of %name% IN INCHES?
CLS
set /p a=What is the age of %name%?
CLS
if %sex%==M set /a result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)
if %sex%==F set /a result=655+(4.35*%w%)+(4.7*%h%)-(4.7*%a%)
echo %result% is the caloric intake for %name%.
pause
exit
#echo off has been disabled for the sake of troubleshooting.
It's a simple program meant to do some caloric intake calculations that are a bit arduous to do one by one.
Every time I reach the point where I need to do arithmetic calculations one of two things happens:
if I wrap the set /a statement in an if %sex%==M ( CODE HERE ) block, the program will exit unexpectedly without showing an error message.
If I don't and have it all on one line (as it is in the example code posted above) then I get the message Unbalanced Parenthesis without any further explanation (thanks Windows...).
As far as I know, mathematically speaking the parenthesis are completely balanced.
I've tried:
wrapping the set /a statement in both "" and [], set /a "result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)" or set /a [result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)]
escaping some or all of the parenthesis used in the arithmetic set /a result=66 + ^(6.23 * %w%^) + ^(12.7 * %h%^) - ^(6.8 * %a%^)
reducing the amount of parenthesis for the sake of testing set /a result=66 + (6.23 * %w%). If I use set /a result=66 + (6.23 * %w%) exactly, I can actually wrap the statement in an if ( CODE HERE ) block and have it actually return an error value. Although the error is still Unbalanced Parenthesis...
lengthening or shortening the number of spaces in the arithmetic itself set /a result=66+(6.23*%w%)+(12.7*%h%)-(6.8*%a%) VS. set /a result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)
all combinations of the above
So I am at a loss as to what could be causing this bizarre behavior.
The error message you are receiving is very confusing in your situation, because the parentheses in the set /A command lines are balanced and work like that in case the entire command line is not placed inside of a parenthesised block of code on its own. Anyway, it is always a good idea to put the entire set /A expression in between quotation marks, so neither parentheses nor other special characters can cause trouble.
Square brackets do not have any particular meaning for set /A.
Anyway, the root cause of the error is the fact that you are using fractional numbers although set /A only supports signed integer arithmetics in a 32-bit room (see the help of set /?). Type set /A (1.2*3) in a command prompt window and you will receive the same error message; but remove the . and everything is going to be fine.
A possible work-around is to use something like fixed-point arithmetics, hence multiplying everything by a whole power of 10 during the calculations and dividing the result by the same power of 10 finally, or in other words, shifting the decimal point to the right before doing the calculations and shifting it back afterwards.
This is how it could look like (I also fixed some other issues in your code, but see below for that):
#echo off
#title Calculate Caloric Needs
cls
set "name=them"
set /P "name=What is the name of the individual in question? "
:START
set "sex="
set /P "sex=Is %name% a M(ale) or F(emale)? (M/F) "
if /I not "%sex%"=="M" if /I not "%sex%"=="F" (
echo Invalid Selection... Try Again...
goto :START
)
set "w=0"
set /P "w=What is the target weight of %name%? "
set "h=0"
set /P "h=What is the height of %name% IN INCHES? "
set "h=%h%." & rem // append a decimal dot to entry in `h`
set "mil=1%h:*.=%" & rem // store everything behind first dot in `mil`, prepend `1`
set /A "h+=0, mil+=0" & rem // convert `h` to integer, dismiss fractional part
set "mil=%mil%000" & rem // pad trailing zeros to `mil`
set "mil=%mil:~,4%" & rem // extract first four numerals from `mil`
set /A "mil+=5" & rem // add `5` to `mil` for rounding
if %mil:~,1% GTR 1 set /A "h+=1" & rem // regard carry of previous addition in `h`
set "h=%h%%mil:~-3,-1%" & rem /* append second and third numeral of `mil` to `h`,
rem hence dismissing previously prepended `1`;
rem so `h` holds the height in 100ths of inches now */
set "a=0"
set /P "a=What is the age of %name%? "
rem // quotation marks avoid trouble with parenthesis or other characters;
rem /* all original constants are multiplied by `1000` to avoid fractional parts,
rem except the factor at `h` which is multiplied by `10` only due to above
rem implicit multiplication of `h` by 100, then `500` is added for rounding,
rem and finally, the result is divided by `1000` to remove the previous factors: */
if /I "%sex%"=="M" (
set /A "result=(66000+(6230*%w%)+(127*%h%)-(6800*%a%)+500)/1000"
) else if /I "%sex%"=="F" (
set /A "result=(655000+(4350*%w%)+(47*%h%)-(4700*%a%)+500)/1000"
)
echo %result% is the caloric intake for %name%.
pause
exit /B
This is what I fixed:
the set and set /P syntax is improved so that entire expression is in between quotation marks, hence you avoid trouble with special characters and you clearly can see if there are any trailing white-spaces;
any prompted values are initialised to avoid the previous value is taken in case the user just presses RETURN;
the if queries for gender entry are corrected so that they are now case-insensitive by adding switch /I, and that they do not cause trouble in case the tested variables are empty by enclosing the comparison expressions in quotation marks;
by the way, you might be interested in the choice command for such single-key entries, because it does not even accept any other characters;
the value of the height entry h in inches is converted to 100ths of inches because of the said fixed-point arithmetics (but weight w and age a are still treated as integers); this is how it works:
set "h=%h%.": append a dot to the entry in h to ensure there is at least one;
set "mil=1%h:*.=%": store everything after the first dot in h into mil (fractional part), and prepend 1 to the result to not lose any leading zeros (which are significant for the fractional part) as soon as this is converted to a number;
set /A "h+=0, mil+=0": convert both h and mil to numeric values (integers); this converts everything up to the first non-numeric figure to a number, ignoring leading white-spaces but regarding signs +/- (although they are irrelevant as not needed here);
set "mil=%mil%000": append 3 trailing zeros to the fractional part (the prepended 1 is still there, so there are always at least 4 digits);
set "mil=%mil:~,4%": extract the first 4 characters (so the prepended 1 plus 3 more figures);
set /A "mil+=5": add 5, so if the last numeral is 5 or more, a carry appears, so rounding up of the next-to-last figure happens;
if %mil:~,1% GTR 1 set /A "h+=1": increment integer part in h in case the carry exceeds the fractional part;
set "h=%h%%mil:~-3,-1%": concatenate the integer part of h and the range from the second to the next-to-last figure of the fractional part in mil and store it in h;
all the constants in the formulas for result are multiplied by 1000 for the decimal dot to disappear, except the factor of h which is multiplied by 10 only, because h is already 100 times the original height value; afterwards the result is divided by 1000; the addition of 500 means nothing but adding 0.5 when regarding the division by 1000 and is intended do round up fractional parts from .5 up to .9* before they get lost due to the integer division;
the exit command is replaced by exit /B to terminate batch file but not the owning cmd instance;
Note that comments like // and /*, */ have no special meanings in batch-files. Comments or remarks are given by the rem statement. I used the slash-style comments within rem here only for some cool syntax highlighting here on this site...
Update
As per a comment by the original poster, the result itself should be a fractional number, so I modified the script a bit (a description follows below):
#echo off
#title Calculate Caloric Needs
cls
set "name=them"
set /P "name=What is the name of the individual in question? "
set "male="
choice /C MF /M "Is %name% a M(ale) of F(emale)? "
if not ErrorLevel 2 set "male=Flag"
set "w=0"
set /P "w=What is the target weight of %name% IN POUNDS? "
set "h=0"
set /P "h=What is the height of %name% IN INCHES? "
set "h=%h%." & rem // append a decimal dot to entry in `h`
set "mil=1%h:*.=%" & rem // store everything behind first dot in `mil`, prepend `1`
set /A "h+=0, mil+=0" & rem // convert `h` to integer, dismiss fractional part
set "mil=%mil%000" & rem // pad trailing zeros to `mil`
set "mil=%mil:~,4%" & rem // extract first four numerals from `mil`
set /A "mil+=5" & rem // add `5` to `mil` for rounding
if %mil:~,1% GTR 1 set /A "h+=1" & rem // regard carry of previous addition in `h`
set "h=%h%%mil:~-3,-1%" & rem /* append second and third numeral of `mil` to `h`,
rem hence dismissing previously prepended `1`;
rem so `h` holds the height in 100ths of inches now */
set "a=0"
set /P "a=What is the age of %name% IN YEARS? "
rem // quotation marks avoid trouble with parenthesis or other characters;
rem /* all original constants are multiplied by `1000` to avoid fractional parts,
rem except the factor at `h` which is multiplied by `10` only due to above
rem implicit multiplication of `h` by 100, then `500` is added for rounding,
rem and finally, the result is divided by `1000` to remove the previous factors: */
if defined male (
set /A "result=66000+(6230*%w%)+(127*%h%)-(6800*%a%)+5"
) else (
set /A "result=655000+(4350*%w%)+(47*%h%)-(4700*%a%)+5"
)
echo %result:~,-3%.%result:~-3,-1% is the caloric intake for %name%.
pause
exit /B
This is what I did:
the basic calculation formulas are still the same, except that the division by 1000 has been omitted this time, and that 5 is added to the result for rounding instead of 500; for displaying the result though, the very last figure is dismissed and a decimal dot . is inserted before the remaining last two numerals, so there are two fractional digits for the result;
the gender entry is now accomplished by the choice command as suggested in the original version of this answer (see above), so there is no more need to capture invalid results as choice simply only accepts predefined keys or characters;
As per #aschipfl's response, this was because I was attempting to use /a with a floating-point number.
I fixed this by using bc for Windows. Because something as basic as a floating point operation should be supported by default in the commandline nowadays.
So what was:
if %sex%==M set /a result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)
is now:
set bc=bc\bc.exe
if /I "%sex%"=="M" echo 66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%) | %bc% & echo is the caloric intake required per-day for %name%
EDIT:
I need to a bat script ( or one-liner) to loop through a range which is given as a padded value. It will:
Unpad the values for start and end
iterate through a range given an unpadded value
that value needs to be re-padded again
For instance: start=0980 and end=1000 and step=1
I need to loop through that range and given a padded number back out.
I have this much but I keep getting:
(
SET VAR=000985
SET VAR=~-4
ECHO
)
ECHO is on.
What I have so far:
REM Get the start, end and step values
SET start=0980
SET end=0985
SET step=1
REM Remove Padding
FOR /F "tokens=* delims=0" %%A IN ("%start%") DO (SET start=%%A)
FOR /F "tokens=* delims=0" %%A IN ("%end%") DO (SET end=%%A)
FOR /l %%x in (%start%, %step%, %end%) DO (
SET VAR=000%%x
SET VAR=%VAR:~-4%
ECHO %VAR%
)
Eventually VAR will be feed into a command
mantra -V a -F /some/path/with.%x.ifd
#echo off
setlocal enabledelayedexpansion
REM Get the start, end and step values
SET /a start=980
SET /a end=985
SET /a step=1
REM Remove Padding
FOR /l %%x in (%start%, %step%, %end%) DO (
SET VAR=000%%x
SET VAR=!VAR:~-4!
ECHO !VAR!
)
Delayedexpansion is the key - Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note the use of set /a since the values to be assigned are pure numeric. Unfortunately, leading zeroes imply the value is OCTAL so dispose of those.
Many, many articles on SO about delayedexpansion...look around.
Your question is confusing. I don't understand if you need a solution that performs precisely the steps described (unpad values, iterate, re-pad value) or that generate a list of padded numbers no matter the method used; also, you did not shown what is the required output, so I assumed that is this list: 0980 0981 0982 0983 0984 0985. Finally, you had not indicated the number of digits in the padded numbers, so I assumed it is 4 and that the input data have that number of digits or more.
This is a simple solution:
#echo off
setlocal EnableDelayedExpansion
REM Get the start, end and step values as padded numbers
SET start=0980
SET end=0985
SET step=1
rem Convert the start and end values into an equivalent "1+N-digits" number (4 in this case)
rem for example: start=10980 and end=10985
set /A start=1%start:~-4%, end=1%end:~-4%
rem Iterate through the new range: from 10980 step 1 to 10985
for /L %%i in (%start%, %step%, %end%) do (
rem Output the digits after the first one; for example: 0980, 0981, etc
echo !start:~1!
rem Pass to next number
set /A start+=step
)
#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
)
I got this code from someone else but cannot understand how it works. This wouldn't be a problem except that I tried to make this batch file applicable for use to be called from another batch, but ended up running into errors. If I could understand how exactly this works to get the length of the string I then will be able to tailor it so I can use it.
You don't necessarily need to tell me what every command does, (I do have some experience), but if you could tell me which commands are used in what order I could look up the ones I don't yet know.
%%P?
!%~2!#?
Just looks like rubbish to me.
A lot of this would become obvious if you sat down for hours and read the output of set /? but, assuming you don't have the time for that (and, let's face it, who on Earth really wants to read documentation?), I'll try and explain.
Specifically, there's a bit in that help text that states %x:S,L% (or the equivalent with delayed expansion, !x:S,L!) means the substring of the x variable, starting at S (zero-based) and L characters long.
The pseudo-code of that function is basically:
def strlen (byref RESULT, byval STRING):
set str to STRING, appending "#" as well
set len = 0
for pos = 4096 to 1 inclusive, halving each time:
while str has a character at position pos:
add pos to len
remove pos characters from start of str
end
end
RESULT = len
The initial string has a character added so as to not be adversely affected by the substring with its first index being zero rather than one. The length of the string will then be the position of that final character.
The rest is relatively straightforward. The len variable is initialised to zero then, starting pos at 4096, if the string has a character at that position (!s:~%%P,1!" NEQ ""), increase len by that much and remove that many characters from the start.
Once the length of the adjusted string gets below 4096, you start working on 2048 and so on until there is only the # left in the string. The length has then been found and you return it (by actually setting the passed in first variable to that value).
So, for the string ABCDEFGHIJK (turned into ABCDEFGHIJK#), nothing would be added to len until pos reached eight. That's because the all the substrings beyond eleven would be empty.
Once pos reached eight however, the one-character substring at that position is I so you add eight to the length (to get eight) and removes the initial eight characters, giving you IJK#. The string is now to short to have another character at position eight, so pos becomes four.
Then no character is found at that offset (the four characters are at offsets zero, one, two and three) so you go immediately to two. There is a character K at that position so the length has two added (to become ten), the string becomes K# and, because there's now no character at offset two, pos becomes one.
With pos at one, there's a character # at that position so the length has one added (to become eleven), and the string becomes #. Now there's no character at position one so the loop exits, and the final length is returned to the caller.
If you have a hard time following that, use the following code which explains it better (with added debug statements):
#echo off
setlocal
set myString=ABCDEFGHIJK
call :strlen result myString
echo %result%
goto :eof
:strlen
setlocal enabledelayedexpansion
set "s=!%~2!#"
echo.String is [!s!]
set len=0
for %%p in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
echo. Position is [%%p], string is [!s!], character is [!s:~%%p,1!]
if "!s:~%%p,1!" NEQ "" (
set /a "len+=%%p"
set s=!s:~%%p!
echo. Found char, len is [!len!], string is now [!s!]
)
)
endlocal && set %~1=%len%
The output you see from that mirrors my explanation above:
String is [ABCDEFGHIJK#]
Position is [4096], string is [ABCDEFGHIJK#], character is []
Position is [2048], string is [ABCDEFGHIJK#], character is []
Position is [1024], string is [ABCDEFGHIJK#], character is []
Position is [512], string is [ABCDEFGHIJK#], character is []
Position is [256], string is [ABCDEFGHIJK#], character is []
Position is [128], string is [ABCDEFGHIJK#], character is []
Position is [64], string is [ABCDEFGHIJK#], character is []
Position is [32], string is [ABCDEFGHIJK#], character is []
Position is [16], string is [ABCDEFGHIJK#], character is []
Position is [8], string is [ABCDEFGHIJK#], character is [I]
Found char, len is [8], string is now [IJK#]
Position is [4], string is [IJK#], character is []
Position is [2], string is [IJK#], character is [K]
Found char, len is [10], string is now [K#]
Position is [1], string is [K#], character is [#]
Found char, len is [11], string is now [#]
11
paxdiablo did a great job explaining how that highly optimized batch script manages to compute the length of a string.
The original algorithm was developed at http://www.dostips.com/forum/viewtopic.php?p=5385, where you can follow how it was developed. There are many variations of the basic algorithm.
It is a laudable goal to understand how it works, but there is no need to understand the algorithm if all you want to do is develop a script that can be used by other scripts to compute the length of a string. However, you do need to understand the basics of how to develop and call batch "functions". There is a great tutorial at http://www.dostips.com/forum/viewtopic.php?p=5385.
I would reverse the order of the arguments, and make the return variable optional. If not specified, then the function can print the value to the screen (stdout). This makes it very convenient to use from the command line.
You can put the following script in a file named STRLEN.BAT, and place that file in a folder that is included in your PATH environment variable. I use c:\utils as a repository for various batch utilities.
STRLEN.BAT
:strlen StrVar [RtnVar]
::
:: Compute the length of the string in variable StrVar
:: and return the result in variable RtnVar.
:: If RtnVar is not specified, then print the result to stdout.
::
:: This code is a derivative of code developed at
:: http://www.dostips.com/forum/viewtopic.php?p=5385
::
#echo off
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
endlocal & if "%~2" equ "" (echo %len%) else set "%~2=%len%"
exit /b
Usage is trivial.
To print out the length of the PATH variable from the command line:
D:\test>strlen path
1330
If you want to use the utility in a batch script, then of course you must use CALL.
#echo off
:: Print the length of PATH to the screen
call strlen path
echo(
:: Store the length of PATH in variable LEN, then show the result
call strlen path len
echo length of PATH = %len%
It is fine to develop a library of utility scripts that can be used by other scripts for your own use. But if you are distributing scripts to be used by others, it is common practice to embed the routines within a single script to make distribution simpler and more reliable.
I am very very new to Windows batch files, so sorry if this is totally obvious.
Basically, in a batch file, I need a triple IF statement that checks if 3 parameters have been given on the command line. If they are there, it uses GOTO to execute more code. If it doesn't have all three, then it echoes the error.
This is the code I have so far, that doesn't work
IF defined %1% (
IF defined %2% (
IF defined %3% (
GOTO copyoutvariables
ELSE GOTO parametererror
)
)
)
:parametererror
Echo You did not enter the right amount of parameters.
:copyoutvariables
Echo Irrelevant Code goes here.
If I enter three parameters then it goes straight to :parametererror.
I think the syntax for the ELSE is wrong. I don't really know where it should go though.
Is there a better way to format the triple IF?
Is there a way to AND my IF's?
And where should the ELSE statement be?
I think it's your bracketing, based on some distant memory.
Try:
IF defined %1% IF defined %2% IF defined %3%
(
GOTO :copyoutvariables
)
ELSE
(
GOTO :parametererror
)
:parametererror
Echo You did not enter the right amount of parameters.
:copyoutvariables
Echo Irrelevant Code goes here.
Hope that helps.
You need a couple more parentheses around the ELSE.
IF defined %1% (
IF defined %2% (
IF defined %3% (
GOTO copyoutvariables
) ELSE (
GOTO parametererror
)
)
)
Your code have several issues:
IF defined can only be applied to environment variables, NOT Batch file parameters.
Your %1% construction is wrong, because %1 is replaced by the first parameter, if any, and the second % is just ignored.
The right way to check if a parameter was given is: IF "%~1" equ "" echo Parameter 1 not given.
You not need to check if both %1 and %2 parameters was given; just to check if %3 is given. This way, your code may be reduced to this one:
.
IF "%~3" neq "" (
GOTO copyoutvariables
) ELSE (
GOTO parametererror
)