Batch Programming (Nested loops) using labels, program does not work - windows

I don't understand why this script doesn't seem to work and doesn't even pause to see where the error is.. Here is the code for the batch file:
I want this batch to output a series of birthdays starting from Ihf0101 till Ihf3112
set /a month=1
:m
if %month% leq 12
(
set /a day=1
:d
if %day% leq 31
(
if %day% leq 9 set birthday=Ihf0%day%
if %day% gtr 9 set birthday=Ihf%day%
if %month% leq 9
(
set birthday=%birthday%0%month%
echo %birthday%
)
if %month% gtr 9
(
set birthday=%birthday%%month%
echo %birthday%
)
set /a day+=1
goto :d
)
set /a month+=1
goto :m
)
pause

Use for /L loops instead:
#echo off
setlocal enabledelayedexpansion
set "y=2017"
for /l %%m in (101,1,112) do (
set "m=%%m"
for /l %%d in (101,1,131) do (
set "d=%%d"
xcopy /d:!m:~1!-!d:~1!-%y% /l . .. >nul 2>&1 && echo lhf!d:~1!!m:~1!
)
)
two little tricks:
- counting from 101 to 131 and taking the last two digits only gives you a two-digit format always (with leading zero for numbers below 10)
- using xcopy to check if the date is really existent (I learned that here) (necessary to know the year for proper calculation of leap-years)

Related

Increase amount of numbers for picking randomly

There is a batch file that looks for Pen=n in the list.txt and changes its value (n) randomly from the given row of ten numbers (set "var[pen]=1 2 3 4 5 6 7 8 9 10"). But if there are more than ten numbers say twenty (set "var[pen]=1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20") or hundred, it is still picking a number from first ten ones ignoring the rest.
#echo off
setlocal EnableDelayedExpansion
set "file=D:\list.txt"
set "temp=D:\temp.txt"
set "var[pen]=1 2 3 4 5 6 7 8 9 10" &
for /L %%i in (1,10,1%time:~-2%) do set "rand=!random!"
(for /F "usebackq tokens=1,2 delims==" %%a in ("%file%") do (
if defined var[%%a] (
call :getRandomValue var="!var[%%a]!"
echo %%a=!var!
) else if "%%b" neq "" (
echo %%a=%%b
) else (
echo %%a
)
)) > "%temp%"
move /Y "%temp%" "%file%"
pause > nul && pause > nul
goto :EOF
:getRandomValue value="list"
set /A "rand=!random:~-1!+1"
for /F "tokens=%rand%" %%v in (%2) do set "%1=%%v"
Any help would be appreciated.
edit:
the list.txt contains a list of stuff like pen paper rubber etc. with the corresponding value next to it.
pen=5
pencil=43
paper=0
rubber=22
what the bat file does is just putting random number picking in up from the row of numbers provided. in the following case it would change the value of pen to a random number from 1 to 10. but if I add some extra numbers more than ten it will then just ignore them.
You forgot to specify the origin post of this problem. In such a post, there is not any specification about how the probabilities must be given, but all the 3 examples provided have 10 probable values, so I assumed that all probabilities have 10 values. The fix is add the number of probabilities each value can have:
#echo off
setlocal EnableDelayedExpansion
rem Define the probabilities for new values
set "value[apple]=0 0 0 0 0 0 0 1 1 1" & set "num[apple]=10" & rem 0 (70%) or 1 (30%)
set "value[peach]=0 0 0 0 0 1 2 3 4 5" & set "num[peach]=10" & rem from 0 (50%) to 5 (the rest 50%)
set "value[banana]=54 68 82 96" & set "num[banana]=4" & rem each 25%
rem Randomize
for /L %%i in (1,10,1%time:~-2%) do set "ran=!random!"
(for /F "tokens=1,2 delims==" %%a in (input.ini) do (
if defined value[%%a] (
call :getRandomValue value=%%a
echo %%a=!value!
) else if "%%b" neq "" (
echo %%a=%%b
) else (
echo %%a
)
)) > output.ini
move /Y output.ini input.ini
goto :EOF
:getRandomValue value=item
set /A "ran=%random%%%num[%2]+1"
for /F "tokens=%ran%" %%v in ("!value[%2]!") do set "%1=%%v"
exit /B
You also forget (again) to provide the real format of the input file, that include [headers]:
[Berries]
Strawberry=1
Blackberry=-13
Blueberry=100
Cherry=6
[Fruits]
apple=0
peach=4
banana=18
orange=-2.5
[Vegetables]
Potato=44
Tomato=2
Onion=0
Garlic=17
EDIT 2022/01/08: New method added as requested in comment
As I already said, you have not specified the rules to define the probable values. I proposed a method that works correctly based on your first example, but then you define values that does not conform with my proposed method (10 possible values, like in your first example). I modified the method and then you invented values that does not conform either: "Why 100 values?" "Because any number above 31 will make the method fail..."
I modified the method (again) so you can define the probabilities via value:percent pairs. Here it is:
#echo off
setlocal EnableDelayedExpansion
rem Define the probabilities for new values as value:percent pairs
set "value[apple]=23:17 68:83" & rem 23 at 17%, 68 at 83%
set "value[peach]=0:50 1:10 2:10 3:10 4:10 5:10" & rem from 0 (50%) to 5 (the rest 50%)
set "value[banana]=54:25 68:25 82:25 96:25" & rem each 25%
rem Randomize
for /L %%i in (1,10,1%time:~-2%) do set "ran=!random!"
(for /F "tokens=1,2 delims==" %%a in (input.ini) do (
if defined value[%%a] (
call :getRandomValue %%a
echo %%a=!value!
) else if "%%b" neq "" (
echo %%a=%%b
) else (
echo %%a
)
)) > output.ini
move /Y output.ini input.ini
goto :EOF
:getRandomValue item
set /A "ran=%random%%%100+1, val=0"
for %%a in (!value[%1]!) do for /F "tokens=1,2 delims=:" %%x in ("%%a") do (
if %ran% gtr !val! set "value=%%x"
set /A val+=%%y
)
exit /B
What you are aiming to do is to randomly index from a list, which requires you to first determine the number of items in the list.
one method of doing so:
#Echo off
Set "var[pen]=a b c d e f g h i"
Rem enable enviroment for !expanison!
Setlocal EnableDelayedExpansion
Rem Build 'array', splitting string on spaces and incrementing array size count
Set _i=1& Set "item[1]=%var[pen]: =" & Set /A "_i+=1" & Set "item[!_i!]=%"
Rem index randomly from known array size [one indexed]
For /f delims^= %%i in ('Set /A !random! %%!_i! + 1')Do Echo(Item:%%i = !Item[%%i]!
Pause

cmd: How to include a maximum run time in the for loop?

I have a long list and i want to split it up in parts that will wait for an external trigger to go on.
At first I tried a for loop that did 100 rows per run.
But because of differences in workload on the pc the amount of time it takes varies.
So i was wondering, is it possible to loop the FOR statement for a set time.
But could not find any example.
After adding the suggestion stephan made to use a for loop with delayed expansion, it now works.
How to include a maximum run time in the for loop?
'''
#ECHO OFF
SETLOCAL enabledelayedexpansion
CLS
set ENDTIME=!TIME!
set function_endtime=!ENDTIME!
for /F "tokens=1-4 delims=:.," %%a in ("%function_endtime%") do (
set /A "start=(((%%a*60)+1%%b %% 100)*60+1%%c %% 100)*100+1%%d %% 100"
)
:: add 4 seconds to current time as end time
set /A function_endtime=start+(4*100)
:: Format the result for use in if loop
set /A hh=function_endtime/(60*60*100),rest=function_endtime%%(60*60*100), mm=rest/(60*100), rest%%=60*100, ss=rest/100, cc=rest%%100
if %hh% lss 10 set hh=0%hh%
if %mm% lss 10 set mm=0%mm%
if %ss% lss 10 set ss=0%ss%
if %cc% lss 10 set cc=0%cc%
set function_endtime=%hh%:%mm%:%ss%.%cc%
echo Finish: %function_endtime% now: %time%
FOR /F "tokens=1,2 delims=," %%A IN (C:\test.txt) DO (
echo %%A %%B
set "last_value=%%B"
if "!time!" geq "%function_endtime%" goto :done
)
:done
echo %time%
'''
you can't put code between goto and the :label I guess you completely misinterpreted my comment. Your code should look like:
...
FOR /F "tokens=1,2 delims=," %%A IN (C:\test.txt) DO (
echo %%A %%B
set "last_value=%%B"
if "!time!" geq "%function_endtime%" goto :done
)
:done
...
(without any testing or checking your logic)

How to get the day of year in a batch file

How can I get the day of year from the current date in a Windows batch file?
I have tried
SET /A dayofyear=(%Date:~0,2%*30.5)+%Date:~3,2%
But it does not work with leap years, and it is always off by a few days. I would not like to use any third-party executables.
If you want the Julian Day Number, you may use the method posted in my accepted answer given at previous link. However, the "day of year" is just a number between 1 and 365 (366 for leap years). The Batch file below correctly calculate it:
#echo off
setlocal EnableDelayedExpansion
set /A i=0, sum=0
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
set /A i+=1
set /A accum[!i!]=sum, sum+=%%a
)
set /A month=1%Date:~0,2%-100, day=1%Date:~3,2%-100, yearMOD4=%Date:~6,4% %% 4
set /A dayOfYear=!accum[%month%]!+day
if %yearMOD4% equ 0 if %month% gtr 2 set /A dayOfYear+=1
echo %dayOfYear%
Note: This relies on the date format MM/DD/YYYY.
EDIT 2020/08/10: Better method added
I modified the method so it now uses wmic to get the date. The new method is also shorten, but no simpler! ;):
#echo off
setlocal
set "daysPerMonth=0 31 28 31 30 31 30 31 31 30 31 30"
for /F "tokens=1-3" %%a in ('wmic Path Win32_LocalTime Get Day^,Month^,Year') do (
set /A "dayOfYear=%%a, month=%%b, leap=!(%%c%%4)*(((month-3)>>31)+1)" 2>NUL
)
set /A "i=1, dayOfYear+=%daysPerMonth: =+(((month-(i+=1))>>31)+1)*%+leap"
echo %dayOfYear%
#Aacini's answer has got two weak points (although it suffices for many applications, and it is a great approach after all!):
the retrieved system date relies on format MM/DD/YYYY, but %Date% is locale-dependent;
the calculation does not fully comply with the definition of leap years (see this article);
The following is a revised version of #Aacini's batch script (described within explanatory remarks):
#echo off
setlocal EnableDelayedExpansion
set /A i=0, sum=0
rem accumulate day-offsets for every month in %accum[1...12]%
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
set /A i+=1
set /A accum[!i!]=sum, sum+=%%a
)
rem check for availability of alternative date value given via (1st) command line argument,
rem just for convenient batch testing
if "%1"=="" (
rem WMIC retrieves current system date/time in standardised format;
rem parse its output by FOR /F, then store it in %CurrDate%
for /F "tokens=2 delims==" %%D in ('wmic OS GET LocalDateTime /VALUE ^| find "="') do (
set CurrDate=%%D
)
) else (
rem apply date value from command line in YYYYMMDD format (not checked for validity)
set CurrDate=%1
)
rem extract %month% and %day%;
set /A month=1%CurrDate:~4,2%-100, day=1%CurrDate:~6,2%-100
rem compute several moduli needed for determining whether year is leap year
set /A yearMOD4=%CurrDate:~0,4% %% 4
set /A yearMOD100=%CurrDate:~0,4% %% 100, yearMOD400=%CurrDate:~0,4% %% 400
rem calculate %dayOfYear% as it were not a leap year
set /A dayOfYear=!accum[%month%]!+day
rem adapt %dayOfYear% only in case %month% is past February (29th)
if %month% gtr 2 (
rem check for leap year and adapt %dayOfYear% accordingly
if %yearMOD4% equ 0 set /A dayOfYear+=1
if %yearMOD400% neq 0 if %yearMOD100% equ 0 set /A dayOfYear-=1
)
rem compound statement to let %dayOfYear% survive SETLOCAL/ENDLOCAL block
endlocal & set dayOfYear=%dayOfYear%
rem return result
echo %dayOfYear%
This use the laps years in editable in loop | for /l %%L in (2020 4 2100) | < or more!
#echo off & setlocal enabledelayedexpansion && set "_cmd=Get Day^,Month^,Year^"
for /l %%L in (2020 4 2100)do set "_array_leap_year_=!_array_leap_year_!%%L,"
for /f "tokens=1-3delims= " %%a in ('wmic Path Win32_LocalTime !_cmd! ^| findstr /r "[0-9]"')do (
set "_yy=%%c" & set "_mm=0%%b" & set "_dd=0%%a" && set "_mm=!_mm:~-2!" & set "_dd=!_dd:~-2!" & set _date=!_yy!_!_mm!_!_dd!)
echo/!_array_leap_year_!|findstr /lic:"!_date:~0,4!," >nul&&(set "_leap_=29" & set "_year_=366")||(set "_leap_=28" & set "_year_=365")
set "_mm_dd_year_=01-31,02-!_leap_!,03-31,04-30,05-31,06-30,07-31,08-31,09-30,10-31,11-30,12-31" && set /a "_cnt=0" & set /a "_loop=!_mm! * 6"
(for /l %%d in (0 6 !_loop!)do set "_sum=!_mm_dd_year_:~%%d,5!" & if "9!_sum:~,2!" lss "9!_mm!" set /a "_day_year_+=!_sum:~-2!")
set /a "_day_year_+=!_dd!" && set /a "_remain=!_day_year_! - !_year_!" && echo/Today: !_date! ^| Day of Year: !_day_year_! ^| Days Remaining: !_remain:-=!
| Result | Today: 2019_04_21 | Day of Year: 111 | Days Remaining: 254
Conventional formatting:
#echo off & setlocal enabledelayedexpansion
set "_cmd=Get Day^,Month^,Year^"
for /l %%L in (2020 4 2100)do set "_array_leap_year_=!_array_leap_year_!%%L,"
for /f "tokens=1-3delims= " %%a in ('wmic Path Win32_LocalTime !_cmd! ^| findstr /r "[0-9]"')do (
set "_yy=%%c"
set "_mm=0%%b"
set "_dd=0%%a"
set "_mm=!_mm:~-2!"
set "_dd=!_dd:~-2!"
set _date=!_yy!_!_mm!_!_dd!
)
echo/!_array_leap_year_!|findstr /lic:"!_date:~0,4!," >nul && (
set "_leap_=29" & set "_year_=366" )||( set "_leap_=28" & set "_year_=365" )
set "_mm_dd_year_=01-31,02-!_leap_!,03-31,04-30,05-31,06-30,07-31,08-31,09-30,10-31,11-30,12-31"
set /a "_loop=!_mm! * 6"
for /l %%d in (0 6 !_loop!)do set "_sum=!_mm_dd_year_:~%%d,5!" && (
if "9!_sum:~,2!" lss "9!_mm!" set /a "_day_year_+=!_sum:~-2!" )
set /a "_day_year_+=!_dd!"
set /a "_remain=!_day_year_! - !_year_!"
echo/Today: !_date! ^| Day of Year: !_day_year_! ^| Days Remaining: !_remain:-=!

Windows Batch File not Validating Year

So I have a windows batch script that validates to see if the date format I entered is in the correct format. Everything is working fine with it except when it gets to the year part of it. It doesn't seem to validate the year in the correct format of xxxx. It will accept any number. Where is it breaking, I can't tell? Fix suggestions? Thank you!
set i=0
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
set /A i+=1
set dpm[!i!]=%%a
)
set /P "inDate=Please insert FNOL date (MM-DD-YYYY format): "
if "%inDate:~2,1%%inDate:~5,1%" neq "--" goto invalidDate
for /F "tokens=1-3 delims=-" %%a in ("%inDate%") do set "MM=%%a" & set "DD=%%b" & set "YYYY=%%c"
ver > NUL
set /A month=1%MM%-100, day=1%DD%-100, year=1%YYYY%-10000, leap=year%%4
if errorlevel 1 goto invalidDate
if not defined dpm[%month%] goto invalidDate
if %leap% equ 0 set dpm[2]=29
if %day% gtr !dpm[%month%]! goto invalidDate
if %day% lss 1 goto invalidDate
SET fnoldate=%YYYY%-%MM%-%DD%
ECHO.
SET /P confirmdate=You entered a FNOL date of "%fnoldate%". Is this correct? [y/n]
ECHO.
You're missing some components of your batch file.
At the top, you should have the following:
#Echo Off
SetLocal EnableDelayedExpansion
Your code has some GoTo references, but it can't go anywhere. You should add the following at the bottom:
GoTo :EOF
:invalidDate
echo Invalid date.
Hmm. Giving an example of a failure would have been useful.
Let's take an 'invalid' year like 14.
Your calculation for 01-01-14 would be year=114-10000
You really need to check that year has length 4 first.
if "%year:~3%=="" goto invaliddate
if not "%year:~4%=="" goto invaliddate
and you are not then checking after year-is-numeric for year in a valid range
Here is a method to validate the year:
#echo off
set year=1980
for /f "delims=1234567890" %%a in ("%year%") do echo you must enter numeric characters only & goto :EOF
if %year% GEQ 1800 if %year% LEQ 2014 (
echo year is between 1800 and 2014
) else (
echo enter a year between 1800 and 2014
)

Need to search a file based on timestamps within the lines of the file using Windows XP/7 batch

I have a file that contains time-stamped lines of text. I need to search for lines that start at a specific time and end at a specific time. I already have the times needed stored in variables. I need to be able to break up the line and the time into tokens, i believe, and search for tokens that are geq "start time" and leq "stop time". I know this requires a FOR loop, but I'm not sure how to break this up. (Also , I have found that 8 and 9 fail when used for inputs. I am working on this, I think by hr_start+100 and hr_stop+100 before compare and -100 after compare, etc.) Any improvements on this are appreciated. My goal is a completely error-free interface.
:time_begin
cls
echo.
echo (Use 24-hour clock)
echo.
:hr_start
set /a hr_start=0
set /p hr_start=What hour to start?
if "%hr_start%"=="b" goto day
if %hr_start% geq 0 (
if %hr_start% leq 23 (
if %hr_start% lss 10 set hr_start=0%hr_start%) else (
echo Invalid hour
goto hr_start))
:min_start
set /a min_start=0
set /p min_start=What minute to start?
if "%min_start%"=="b" goto hr_start
if %min_start% geq 0 (
if %min_start% leq 59 (
if %min_start% lss 10 set min_start=0%min_start%) else (
echo Invalid minute
goto min_start))
:sec_start
set /a sec_start=0
set /p sec_start=What second to start?
if "%sec_start%"=="b" goto min_start
if %sec_start% geq 0 (
if %sec_start% leq 59 (
if %sec_start% lss 10 set sec_start=0%sec_start%) else (
echo Invalid second
goto sec_start))
echo.
echo.
:hr_stop
set /a hr_stop=23
set /p hr_stop=What hour to stop?
if "%hr_stop%"=="b" goto sec_start
if %hr_stop% geq 0 (
if %hr_stop% leq 23 (
if %hr_stop% lss 10 set hr_stop=0%hr_stop%) else (
echo Invalid hour
goto hr_stop))
:min_stop
set min_stop=59
set /p min_stop=What minute to stop?
if "%min_stop%"=="b" goto hr_stop
if %min_stop% geq 0 (
if %min_stop% leq 59 (
if %min_stop% lss 10 set min_stop=0%min_stop%) else (
echo Invalid minute
goto min_stop))
:sec_stop
set /a sec_stop=59
set /p sec_stop=What second to stop?
if "%sec_stop%"=="b" goto min_stop
if %sec_stop% geq 0 (
if %sec_stop% leq 59 (
if %sec_stop% lss 10 set sec_stop=0%sec_stop%) else (
echo Invalid second
goto sec_stop))
echo.
set start_disp=%hr_start%:%min_start%:%sec_start%
set stop_disp=%hr_stop%:%min_stop%:%sec_stop%
echo Start time is %start_disp%.
echo Stop time is %stop_disp%.
echo.
:compare
set start_hr=%hr_start%*10000
set /a start_min=%min_start%*100
set /a stop_hr=%hr_stop%*10000
set /a stop_min=%min_stop%*100
set /a start_time=%start_hr% + %start_min% + %sec_start%
set /a stop_time=%stop_hr% + %stop_min% + %sec_stop%
if %stop_time% leq %start_time% (
echo End time must be at least one second after start time
pause
goto time_begin)
:time_confirm
echo.
set start_disp=%hr_start%:%min_start%:%sec_start%
set stop_disp=%hr_stop:~-2%:%min_stop%:%sec_stop%
echo Start time is %start_disp%.
echo Stop time is %stop_disp%.
echo.
set corr_time=blank
set /p corr_time=Times are correct?
if /i "%corr_time%"=="y" goto summary
if /i "%corr_time%"=="n" goto time_begin
goto time_confirm
As you can see, I have two ways I could search: an integer using hour, minute, and second, or a string using %start_disp% and %stop_disp%. Here is a sample line, and all lines follow the same pattern exactly up to this point in the line; after the final ], the pattern varies:
2013/10/30 00:00:13 [501014]CODELINE_INDICATION_MSG 192.168.013.254:5501 TX 41 bytes
solution with sed for Windows:
input file is sorted already:
sed -n "\#START TIME#,\#STOP TIME#p" input.txt>output.txt
input file is not sorted:
sort input.txt | sed -n "\#START TIME#,\#STOP TIME#p" >output.txt
example for a sorted file:
sed -n "\#2013/10/30 00:00:13#,\#2013/11/30 00:00:13#p" input.txt>output.txt

Resources