I need this batch file to be interpreted for a beginner - windows

#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.

Related

How to count from 1 to 50 and set each number as a variable in Batch file?

Good Day, I here a batch script that uses a function to operate
#echo off
setlocal enableDelayedExpansion
:INSTALLER
set "n1=7_Zip"
set "n2=Adobe_Products"
set "n3=Allavsoft"
set "n4=Astute_Graphics"
set "n5=AutoHotkey"
set "n6=Backup_and_Sync_from_Google"
set "n7=BlueStacks_5_Beta"
set "n8=CC_Cleaner"
set "n9=Core_Temp"
set "n10=CPUID_CPU-Z"
I'm having trouble because I want the variable n1 n2...10 to be dependent, Meaning I want it to be like this !n%c%! (where %c%is the number after n) so when I insert a new program between 2 programs the numbering will be moved. For example, I will insert the Notepad++ between CC_cleaner and Core_Temp. Now when I insert the Notepad++ its number will be the Old number of Core_Temp which is 9 and the New number of Core_Temp will be 10 and the New number of the CPUID_CPU-Z will be 11. I just can't figure out where I can get the variable 1 2 ... 11 to be substituted to the value of %c%. I'm thinking of a for loop that will count from 1 to 50 and set each number as a variable so I can Substitute those variables for the value of %c% but I don't know how to make it.
I'm also open to other options aside from for loop
use a function to define the array. In doing so, new values only need to be added to the list of parameter values the function is called with.
Edit: I'm not sure whats unclear given the usage example provided and the description of the argument structure of the function, so heres a desciption of the functionality of the function:
In your opening question, you manually define an array with the prefix n followed by numeric indexes one at a time:
set "n1=7_Zip"
set "n2=Adobe_Products"
set "n3=Allavsoft"
set "n4=Astute_Graphics"
set "n5=AutoHotkey"
set "n6=Backup_and_Sync_from_Google"
set "n7=BlueStacks_5_Beta"
set "n8=CC_Cleaner"
set "n9=Core_Temp"
set "n10=CPUID_CPU-Z"
The above method of hard coding each value to an index makes adding values to the beginning / middle of the array time consuming as each n# has to be manually updated.
Rather than hardcoding each value line by line, The function below takes a list (or series of lists) as parameters containing the arrays name (%1, the first argument) and the values to be defined to the array (all other arguments), assigns the list to a variable in order to seperate the array name from the values to be defined, then uses a For loop to iterate over the values, increment the arrays unique index, and then assigns the current value in the list to the appropriate index.
This allows the above definitions to be achieved using the following call:
Call :DefineArray n 7_Zip Adobe_Products Allavsoft Astute_Graphics AutoHotkey Backup_and_Sync_from_Google BlueStacks_5_Beta CC_Cleaner Core_Temp CPUID_CPU-Z
Example output (with Set n.):
n.1=7_Zip
n.10=CPUID_CPU-Z
n.2=Adobe_Products
n.3=Allavsoft
n.4=Astute_Graphics
n.5=AutoHotkey
n.6=Backup_and_Sync_from_Google
n.7=BlueStacks_5_Beta
n.8=CC_Cleaner
n.9=Core_Temp
Notes:
The function defines array variables using an additional . suffix to the arrays prefix name (IE: n.1 n.2 ...). This is done to allow differentiation of the array from other environment variables that begin with the same prefix as the arrays variable name when the Set command is being used.
The function does not zero the index count of the array when called. This allows calls to define values to be spread over multiple lines for easier maintenance and readablity.
#Echo off
:# prepare envarinoment for use of '!' expansion during code blocks
:# '!' expansion allows access of variables whose name contains other variables
:# and provides protection against batch poison characters
Setlocal EnableExtensions EnableDelayedExpansion
:# Calls the label DefineArray with parameters; doublequoting any string containing
:# standard delimiters
Call :DefineArray arrayname value1 "value 2"
:# Display defined variables prefixed with arrayname
Set arrayname
Goto :Eof
:# Function label name and arg structure
:DefineArray groupname list of values
:# Define all paramters to Params with leading whitespace used to remove groupname from parameter list.
Set ^"Params= %*"
:# Remove the groupname from the list of elements to be assigned
Set "Params=!Params: %1 =!"
:# Initialise array index variable specific to the groupname;
:# [If not already incremented]
Set /A "%~1[i]+=0"
:# iterate over Params list; increment group index count; Assign element
:# to groupname.index
For %%G in (!Params!)Do (
Set /A "%~1[i]+=1"
Set "%~1.!%~1[i]!=%%~G"
)
:# exit function
Exit /b 0
Note: The method used above will consume any ! characters present in values due to Delayed expansion.

Reading unknown size matrix to Fortran [duplicate]

How can I read the data file containing known number of lines but the number of entries in each line is unknown, e.g. if my data file contain some thing like
1 3 4 5 6 -7 8 -9
1 3 5 6
4 5 6 7 8 3 5 6 7 8 4 5 7 8
i.e. three lines but the data in each line is unknown. At one time I need the data from one line.
One method: read the line into a string, using a string that is at least as long as the longest expected line. Then you go about parsing the string. E.g., if the numbers are always split by spaces, use that to figure out the substring boundaries. Then you can use "internal reads" to read from each sub-string to obtain the numeric values. An internal read uses a string instead of a unit number and obtains the data from the string -- at least you don't have to recreate the conversion of characters to numeric values, the read statement will do that for you. The intrinsic functions provided with Fortran will make the parsing easier.
An implementation based on what M. S. B. pointed out. Quite late, but I guess it could be useful to someone.
Have an array of the type you expect to read ready:
double precision, dimension(MAX_NUM_OF_COLS) :: test_array
Read a line from your file:
READ(reading_unit,'(A)',iostat=io) line
Loop and try to read from the line a maximum quantity of numbers:
do i=1,MAX_NUM_OF_COLS
READ(line, *, iostat=io) test_array(1:i)
if(io==0) exit
enddo
write(*,*) 'number of columns = ', (i-1)
If needed, loop this over all the lines of your file, and keep the maximum or minimum number of columns.
Minimum example:
integer, parameter :: MAX_NUM_OF_COLS=30
integer, parameter :: MAX_LINE_LENGTH=1000
character(len=MAX_LINE_LENGTH) line
integer i, io, reading_unit
double precision, dimension(MAX_NUM_OF_COLS) :: test_array
reading_unit=100
OPEN(reading_unit, file='the_file')
! Get first line of file.
DO
READ(reading_unit,'(A)',iostat=io) line
IF (io/=0) then
write(*,*) "Error reading file."
stop
endif
exit ! Eventually, do not exit and put the DO loop below here.
ENDDO
CLOSE(reading_unit)
do i=1,MAX_NUM_OF_COLS
READ(line,*,iostat=io) test_array(1:i)
if(io==-1) exit
enddo
write(*,*) 'number of columns = ', (i-1)
Assuming that you're okay with padding the array with zeros (specifically referring to the later duplicate question here), this is my idea:
Read the data line by line into a string, then append a number of zeros, finally read each row of the data from this array. Here's an example:
program unknown_numbers
implicit none
integer, parameter :: nrow=3, ncol=14
integer :: data(ncol, nrow)
character(len=2*ncol) :: zeros ! will contain lots of zeros
character(len=10*ncol) :: line ! temporary storage for each line of the file
integer :: i, u
! Write " 0 0 0 0 0 0 0 0" into the string "zeros"
write(zeros, '(*(I2))') [(0, i=1, ncol)]
open(newunit=u, file='data.txt', status='old', action='read')
do i = 1, nrow, 1
! Read the next line into a temporary string array
read(u, '(A)') line
! Append a number of zeros to the temporary string
line = trim(line) // zeros
! Read the row of data from the string.
read(line, *) data(:, i)
end do
close(u)
! For testing purposes, print the data.
print '(14(X, I3))', data
end program unknown_numbers
integer,parameter :: reclen=99999 ! maximum record length
integer,parameter :: undef=-9999 ! undefined value
integer :: actual_reclen ! actual record length
integer,dimension(reclen) :: dummy=undef ! dummy array used for reading
integer,dimension(:),allocatable :: a ! final array
open(unit=10,file='sample.txt',form='formatted',access='sequential')
read(unit=10,fmt=*,end=101)(dummy(i),i=1,reclen)
101 close(unit=10)
actual_reclen=COUNT(dummy/=undef)
allocate(a(actual_reclen))
a=dummy(1:actual_reclen)
end
This is a program that can count numbers in a line (or number of columns) but for a line. If you have many lines, you should change it slightly.
program test12
implicit none
integer n,m,i
integer,allocatable::x(:)
open(10,file='C:\Users\user\Desktop\file.txt')
allocate(x(n))
20 n=n+1
deallocate(x)
allocate(x(n))
read(10,*,iostat=m)(x(i),i=1,n)
if (m==-1)then
goto 30
else
rewind 10
goto 20
end if
30 print*,n-1
end

Batch File "Unbalanced Parenthesis"

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%

Windows Bat: Loop a Padded Range

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
)

Windows Batch: Assign values from 1 character to another in a string to a variable

I was wondering how to assign all the text from 1 point in a string to another point in the same string, to a variable.
For example:
set special=K
set string=don't_add_meKadd_meKdon't_add_me
I was just wondering how to "extract" the add_me and assign it to a variable.
You can use substrings to get the part of the string if you know exactly where in the string it is.
set string=don't_add_meKadd_meKdon't_add_me
set add_me_part=%string:~13,6%
Or, since it seems you're using K as a delimiter, you can use a for loop:
set string=don't_add_meKadd_meKdon't_add_me
for "delims=K" %%A in ("%string%") do set add_me_part=%%B

Resources