0

Background

I run a batch file at night (after 0:00) that needs to be run for a few hours.

For this, there is a simple loop:

for /l %%t in (1, 1, %1) do (
   REM some commands that take a few minutes
)
REM some more commands that take a few minutes

The problem

When I reach a certain hour of the day, in the morning (say, 6:05), I want to break the loop and do something else.

What I've found

Sadly the only thing I've found about time is this one:

How do I get current date/time on the Windows command line in a suitable format for usage in a file/folder name?

But I don't understand how I can use the current time, compare it to a given time, and check which is larger.

As for breaking a loop, I'm not sure what's the official one, as I've found multiple solutions. Maybe "goto" is the best one, as shown here:

https://stackoverflow.com/a/60401897/878126

The question

Inside the loop, how can I compare the current time to a specific time of my choice (6:05 in the morning for this case), and break the loop, going further to other commands?

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • How could I use it to break the loop, exactly? The batch file is doing its job, and I want to break the loop, and then continue to other commands below it. How would you use the Windows Task Scheduler for it? The batch file can communicate with it somehow? Or be triggered by it? – android developer Oct 21 '22 at 15:12

1 Answers1

0
for /l %%t in (1, 1, %1) do (
   REM some commands that take a few minutes

   CALL :shouldistop
   if defined breakloop echo Loop broken when T=%%t&goto nextstep

)

:nextstep

REM some more commands that take a few minutes

....
GOTO :eof

:shouldistop
:: Assume time format is h:mm:ss.cc (24hr; suppressed-leading-zero for hours)
set    "breakloop=%time::=%"
set /a  breakloop=%breakloop:.=% / 10000
if %breakloop% gtr 605 goto :eof
set "breakloop="
goto :eof

Much depends on your time format. My time format is, eg 23:12:06.26 and that's the string that appears in the magic variable time for me.

This code assumes that you use suppressed-leading-zero for the hour. Suppose we use 6:12:17.22 to trace how it works.

breakloop is initialised to 6:12:17.22 with the colons removed = 61217.22 (see set /? from the prompt for documentation, or there are thousands of examples on SO)

breakloop then has its . removed, yielding 6121722 and this is divided by 10000, setting breakloop to 612

Compare 612 to 605 - this is true, so goto end-of-file; otherwise, "set" the value of breakloop to nothing which removes it from the environment (it becomes undefined).

Note that if you want to include seconds, then you'd use /100 and 60530.

If you have leading-zeroes for the hour, then set breakloop to 1%time::=% initially, and use 10605 for the time (ie force-prefix the time with a 1 to ensure the calculation is performed in decimal, not octal)

=== without using subroutine

for /l %%t in (1, 1, %1) do (
   REM some commands that take a few minutes

   setlocal enabledelayedexpansion
   set "now=!time!"
   rem see whether hours is less than 10
   if "!now:~1,1!==":" set "now=0!now!"
   if "!now:~0,5!" gtr "06:05" endlocal& goto nextstep
   endlocal

)
:nextstep
REM some more commands that take a few minutes

Use !now:~0,8! to compare against 06:05:30

The issue here is that a string comparison needs to be invoked if the data contains anything other than numeric characters and therefore we need to leading-zero-pad any time before 10:00 since a string-comparison is done character-by-character so 11:20 is LESS than 6:05 as 1 is less than 6.

The compare time must be in HH:MM format.

And we need to deal with extracting current time within a block, hence we need to invoke setlocal to allow !var! syntax and keep the endlocal commands balanced with the setlocal.

Much easier to use a subroutine, IMHO.

Magoo
  • 77,302
  • 8
  • 62
  • 84
  • This looks very promising. As I'm quite new about advanced batch files on Windows (I'm much more experienced in Java*Kotlin), can you please explain what you did at the bottom? To me it seems like a function that you've let the code around it to skip it. Am I correct? Could you put the logic directly inside the place I've put, instead? You can create a duplicate code to show both ways, if you wish... Also, is there a way to make it work no matter the time format? On Java/Kotlin you could choose the format. I want to make sure it works... – android developer Oct 21 '22 at 17:23
  • Batch simply executes line-by-line until it reaches a `goto`, `exit` or `call` statement, so implementing an internal subroutine needs to be guarded from flow-through. An internal subroutine is faster than an external subroutine which must be in a different file. The difference is between `call :routinenamel` (internal) and `call routinename` (external executable). This construct is convenient as it avoids the problems of accessing variables that change within a (parenthesised block). It would be *possible* see [Stephan's DELAYEDEXPANSION link](https://stackoverflow.com/a/30284028/2128947) – Magoo Oct 21 '22 at 17:32
  • So many time formats - theoretically possible, but extraordinarily awkward, as the `time` format may be 12/24 hr, may contain different punctuation and may be followed by a number of different combinations of am/pm indicators. I can't guarantee to cover them all - but it's an idea I may give thought to. There's two schools of thought - customise to suit the format(s) that the user uses (hence, ask for format specs) or generate a slab of code to normalise it when most of the code will not be used (but will likely need to be explained). Same comment goes for date-manipulation. – Magoo Oct 21 '22 at 17:40
  • So it is like a function, that you call from somewhere else. Can you please provide the same code, but without it, meaning directly check in the place I've presented? As for the modification of the time into a number that you check, this seems a bit unconventional (merging the hour,minute,second together to a number that is more like a string), no? Isn't there a better way? Meaning to have 2 variables, one of the hour, and one of the minutes, and then compare them both? I hope this will work... – android developer Oct 21 '22 at 20:26
  • What is the "setlocal enabledelayedexpansion" for? – android developer Dec 18 '22 at 12:12
  • Within a `for` loop, any `%var%` will be resolved to the value `var` had when the `for` was encountered, even if its value is changed within the loop. That statement invokes `delayedexpansion` mode where `%var%` is the *original* value and `!var!` is the *current* ("run time") value. See [Stephan's DELAYEDEXPANSION link](https://stackoverflow.com/a/30284028/2128947) – Magoo Dec 18 '22 at 16:53