Batch file IF GEQ triggers despite not matching - windows

I'm trying to start and .exe at a certain time frame, but despite not matching GEQ yet (e.g. 20:55), it still goes to the :run label.
I've also tried only EQU, but here, despite matching the time, it doesn't goto :run. Only if i start the batch at the exact %time%, it works, but obviously that's missing the whole point.
Whats wrong here?
#ECHO OFF
SET hour=%time:~0,5%
echo It is %hour%
:check
echo.
echo %time:~0,5%
echo checking
timeout /t 60
IF %hour% GEQ 21:00 IF %hour% LEQ 22:00 (goto :run) else (goto :check)
:run
echo running
start chrome.exe
pause

May be you can try something like this:
#echo off
:get_the_time
for /f %%# in ('wMIC Path Win32_LocalTime Get /Format:value') do #for /f %%# in ("%%#") do #set %%#
echo CURRENT TIME -- %hour%:%minute%
:: creatring a comparable number with wich time for starting chrome can be used
if %hour% LSS 10 ( set hour=10%hour% ) else ( set hour=1%hour%)
if %minute% LSS 10 (set minute=0%minute%)
set comparable_time=%hour%%minute%
::now the comparable_time is in format 1HourMinute . The 1 in the front is to avoid complications with leading zero
timeout /t 60
if %comparable_time% GEQ 12100 if %comparable_time% LEQ 12200 ( goto :run ) else ( goto :get_the_time )
:run
echo running
start chrome.exe
pause
As your method of getting time is not so robust and depends on the time settings in the control panel I preferred using the WMIC.
When there are non numerical symbols in the things you want to compare with IF it will make an alphabetical comparison so the : is not part of the IF clauses now.
Also a comparison is made with variable looking like 1Hourminute and the hour and minutes are kept in two digits format so now only a comparisons with numbers without leading zeros are used.

It is easy enough to handle dates in PowerShell. Create two files in the same directory.
=== runitat.ps1
$h = (Get-Date).Hour
if (($h -ge 20) -and ($h -le 21)) {
Invoke-Command -Command {& "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"}
}
=== runitat.bat
:head
powershell -NoLogo -NoProfile -File %~dp0runitat.ps1
timeout /T 60
GOTO head

Related

Get out of loop if more than one minute Windows Batch file

I am trying to delete a folder on Windows server if a certain condition is met. If it is not met, then a wait for 10 seconds and loop around, check for the condition again. I also need to make sure that I am not in the loop forever. (Check if I am in the loop for more than 60 seconds, then get out of the loop). The batch file looks something like this:
C:\postgresql\uninstall-postgresql.exe --mode unattended
set TIMESTAMP1=%TIME%
:deleteFolder
tasklist /V |findstr /i "_uninstall*" >nul
if %errorlevel% == 0 (timeout /T /10 >nul
set TIMESTAMP2=%TIME%
**REM I want to make sure that we get out of this loop if the diff b/w TIMESTAMP2
AND TIMESTAMP1 IS MORE THEN 60 SECONDS**
goto deleteFolder
) ELSE (
if exists C:\postgresql RD /Q /S C:\postgresql)
Command 1
Command 2
Command 3
So, I am trying to uninstall Postgresql from a windows server, making sure that the uninstall is complete by checking the tasklist and then delete the basedir (C:\postgresql). If the uninstall process is still running, then wait for 10 seconds and check the tasklist again. I just want to make sure that I am not stuck in the loop forever.
Thanks in advance
Using timestamps and calculating time difference:
#echo off
setlocal EnableDelayedExpansion
C:\postgresql\uninstall-postgresql.exe --mode unattended
set "startTime=%time: =0%"
set "endTime="
:deleteFolder
tasklist /V |findstr /i "_uninstall*" >nul
if %errorlevel% == 0 (
goto waitAndDeleteFolder
) else (
goto cleanup
)
:waitAndDeleteFolder
timeout /T 10
set "endTime=%time: =0%"
set "end=!endTime:%time:~8,1%=%%100)*100+1!" & set "start=!startTime:%time:~8,1%=%%100)*100+1!"
set /A "elap=((((10!end:%time:~2,1%=%%100)*60+1!%%100)-((((10!start:%time:~2,1%=%%100)*60+1!%%100), elap-=(elap>>31)*24*60*60*100"
if !elap! gtr 6000 goto done
goto deleteFolder
:cleanup
if exist "C:\postgresql" RD /Q /S "C:\postgresql"
goto done
:done
The simpler method would be to utitlise a count to break the loop
#Echo off & Set Count=0
C:\postgresql\uninstall-postgresql.exe --mode unattended
:deleteFolder
If "%Count%"=="6" Goto :Failed
tasklist /V |%__AppDir_%findstr.exe /lic:"_uninstall" >nul 2> Nul && (Timeout /T 10 /Nobreak > Nul & <Nul Set /P "=." & Set /A "count+=1" & Goto :deleteFolder) || Goto :Post
:Post
if exist C:\postgresql (
RD /Q /S C:\postgresql && Echo/Task Completed
) Else Echo/C:\postgresql Absent
Goto :Eof
:Failed
Echo/Task failed to complete in the allocated time
Goto :Eof
I would however suggest a more robust approach on your part to identifying the task

Windows CMD - Code Statement As Value of Array Variable Not Working For IF Command

I want to create an automated testing array in Windows Command using a single code statement as the VALUE for each testing array record. Here is what the array definition looks like for the first two records in the $code_test[xx] array:
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
I want to execute the VALUE of each $code_test[xx] array record (which is a test code statement) using a for /l loop like this:
for /l %%g in (0,1,1) do (
echo $code_test[0%%g]
!$code_test[0%%g]!
echo.
)
The complete code is:
#echo off
setlocal enabledelayedexpansion
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
for /l %%g in (0,1,1) do (
echo $code_test[0%%g]
!$code_test[0%%g]!
echo.
)
echo.
pause
When I execute the complete code I get the following error message when the !$code_test[0%%g]! line of code is executed:
'if' is not recognized as an internal or external command, operable
program or batch file
I've read the excellent article here about how the IF command is parsed, but I don't see anything that jumps out at me as to why my code is failing. Is it even possible to do what I'm trying to accomplish?
Any Help Is Appreciated!
UPDATE:
I discovered that #sst is correct when I used the set command in the set $code_test[01]=if 5 lss 10 (set /a $var1+=1)
line of code:
#echo off
setlocal enabledelayedexpansion
set $var1=0
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (set /a $var1+=1)
for /l %%g in (0,1,1) do (
echo $code_test[0%%g]
cmd /c !$code_test[0%%g]!
echo $var1 = !$var1!
echo.
)
echo.
pause
The set /a $var1+=1 command didn't increment the value of $var1 as expected. In fact, it places a "1" in front of the $var1 label when the line of code echo $var1 = !$var1! executes. I haven't tried the solution offered by #Magoo yet. I'd really like to use the cmd /c solution instead of calling a subroutine. Is there anything I can do to make the set command work with the cmd /c option?
It can not work the way you tried, because the IF command is detected and handeled in phase 2.
But with delayed expansion it is too late.
This problem occurs only for the commands IF, FOR and REM.
´CALL %%$code_test%%` works in many cases, but fails also for the three commands and for code blocks.
It's unclear why it fails in that case, as the commands are parsed in phase 2 but they are not recognized.
The solution of #stephan uses a new cmd instance, it has the drawback, to lose all variable modifications every time the cmd instance finishes.
The solution of #Magoo uses therefore percent expansion, that should work in nearly all cases without problems.
#echo off
setlocal enabledelayedexpansion
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
for /l %%g in (0,1,1) do (
SET "$code_test=!$code_test[0%%g]!
CALL :test_code
echo.
)
echo.
GOTO :EOF
:test_code
%$code_test%
GOTO :eof
Another approach.
you seem to need another layer of parsing:
#echo off
setlocal enabledelayedexpansion
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
for /l %%g in (0,1,1) do (
echo $code_test[0%%g]
cmd /c !$code_test[0%%g]!
echo.
)
Edit (just for completeness):
found another way to do it (without a subroutine):
#echo off
setlocal enabledelayedexpansion
set $code_test[00]=if 5 lss 10 (echo IT WORKED)
set $code_test[01]=if 5 lss 10 (echo BUG OFF)
for /l %%g in (0,1,1) do (
echo $code_test[0%%g]
for /f "delims=" %%a in ('%%$code_test[0%%g]%%') do #set "result=%%a"
echo !result!
echo.
)
The trick is: for executes the ('...') part in a separate cmd process (as cmd /c ...) but captures the output into the main process.
Works both with !$code_test[0%%g]! and %%$code_test[0%%g]%%.
Although it doesn't work with if 5 lss 10 (set result=IT WORKED) because there is no output to capture (the set is only done with the do clause).

Implementing timed input in batch file. (countdown to a minute)

I am using windows 8 to write a batch file and I got stuck in implementing the timer in batch file. I want to ask input from the user and give them one minute to type their input. Once the time hit a minute then display message like 'the time is over'. So, the time will start from 1 second and ends at 60 seconds OR start from 60 seconds and going down to 0 second. Either works just fine.
Additionally, I want timer to be displayed somewhere on screen so that they can see the countdown. Also, while the timer is running I want the user to be able to type a word and hit enter. This program will not make user wait, but it will wait until the time is over OR as soon as the user enter a word (whichever comes first). After they enter a valid word then I want to store that word in certain variable and do something like (goto VALIDWORD OR echo That is a valid word!)
I don't know if this is possible in batch file and there are more advanced language to use, but I want to complete this program using batch scripting. Thank you.
Following is my concept:
#echo off
:Start
color EC
set /a time =60
:loop
cls
if %time% EQU 0 goto Timesup
if %time% LEQ 60 goto CONTINUE
:CONTINUE
ping localhost -n 2 >nul
set /a time-=1
set /p cho= Enter your word:
echo Remaing Time: %time%
goto loop
:Timesup
echo The time is over!
exit
Any ideas or support would be appreciated. Again Thanks!
Batch files are not designed for tasks like this one, but it is possible to perform certain advanced managements although in a limited manner. The subroutine below use choice command to get input keys, so it does not allow to end the input with Enter key nor to delete characters with BackSpace; it use 0 and 1 keys for such tasks.
#echo off
setlocal
call :ReadTimedInput 60 word=
echo Word read: "%word%"
goto :EOF
:ReadTimedInput seconds result=
setlocal EnableDelayedExpansion
set /A seconds=100+%1
set "letters=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"
echo Enter a word; press 0 to End, press 1 to Clear
:clear
set /P "=X!CR! !CR!" < NUL
set "result="
:loop
set /P "=X!BS! !CR!%seconds:~-2%: %result%" < NUL
choice /C ¡10%letters% /N /CS /T 1 /D ¡ > NUL
if %errorlevel% equ 1 goto tick
if %errorlevel% equ 2 goto clear
if %errorlevel% equ 3 echo/& goto endInput
set /A char=%errorlevel%-4
set "result=%result%!letters:~%char%,1!"
:tick
set /A seconds-=1
if %seconds% gtr 100 goto loop
echo !CR!00
set "result=Time out"
:endInput
endlocal & set "%2=%result%"
exit /B

Batch: Trying to get a command to execute at a specific amount of seconds

This works for hours, running in a loop as soon as it hits 13 hours it executes.
#ECHO OFF
:time
echo %time%
FOR /f "tokens=1*delims=0" %%a IN ("$0%time:~0,2%") DO SET /a HH=%%b
IF %HH% equ 13 goto success
goto time
:success
echo success finally
pause
goto time
but if I run this for seconds in a loop for it to execute at 13 seconds it just keeps running in a loop and doesn't execute the command.
#ECHO OFF
:time
echo %time%
FOR /f "tokens=1*delims=0" %%a IN ("$0%time:~5,2%") DO SET /a SS=%%b
IF %SS% equ 13 goto success
goto time
:success
echo success finally
pause
goto time
This Doesn't Work either
FOR /f "tokens=1*delims=0" %%a IN ("$0%time:~6,2%") DO SET /a SS=%%b
I need this to execute at any specified amount of seconds. So that instead of for instance executing at 13:00:00.00. I can get it to execute at 13:00:13.00 or something like that.
Needs to be a 6 not a 5 for the offset.
%time:~6,2%
It does not have to be complex. This works, but unless you provide more details as to what you are trying to accomplish we cannot provide the best help.
:time
echo %time%
IF %time:~6,2% equ 13 goto success
goto time
Note that using the time environment variable is user locale specific.
Use something like for no locale format time.
wmic path win32_operatingsystem get LocalDateTime

While loop in batch

Here is what I want, inside the BACKUPDIR, I want to execute cscript /nologo c:\deletefile.vbs %BACKUPDIR% until number of files inside the folder is greater than 21(countfiles holds it).
Here is my code:
#echo off
SET BACKUPDIR=C:\test
for /f %%x in ('dir %BACKUPDIR% /b ^| find /v /c "::"') do set countfiles=%%x
for %countfiles% GTR 21 (
cscript /nologo c:\deletefile.vbs %BACKUPDIR%
set /a countfiles-=%countfiles%
)
set /a countfiles-=%countfiles%
This will set countfiles to 0. I think you want to decrease it by 1, so use this instead:
set /a countfiles-=1
I'm not sure if the for loop will work, better try something like this:
:loop
cscript /nologo c:\deletefile.vbs %BACKUPDIR%
set /a countfiles-=1
if %countfiles% GTR 21 goto loop
A while loop can be simulated in cmd.exe with:
:still_more_files
if %countfiles% leq 21 (
rem change countfile here
goto :still_more_files
)
For example, the following script:
#echo off
setlocal enableextensions enabledelayedexpansion
set /a "x = 0"
:more_to_process
if %x% leq 5 (
echo %x%
set /a "x = x + 1"
goto :more_to_process
)
endlocal
outputs:
0
1
2
3
4
5
For your particular case, I would start with the following. Your initial description was a little confusing. I'm assuming you want to delete files in that directory until there's 20 or less:
#echo off
set backupdir=c:\test
:more_files_to_process
for /f %%x in ('dir %backupdir% /b ^| find /v /c "::"') do set num=%%x
if %num% gtr 20 (
cscript /nologo c:\deletefile.vbs %backupdir%
goto :more_files_to_process
)
I have a trick for doing this!
I came up with this method because while on the CLI, it's not possible to use the methods provided in the other answers here and it had always bugged me.
I call this the "Do Until Break" or "Infinite" Loop:
Basic Example
FOR /L %L IN (0,0,1) DO #(
ECHO. Counter always 0, See "%L" = "0" - Waiting a split second&ping -n 1 127.0.0.1>NUL )
This is truly an infinite loop!
This is useful for monitoring something in a CMD window, and allows you to use CTRL+C to break it when you're done.
Want to Have a counter?
Either use SET /A OR You can modify the FOR /L Loop to do the counting and still be infinite (Note, BOTH of these methods have a 32bit integer overflow)
SET /A Method:
FOR /L %L IN (0,0,1) DO #(
SET /A "#+=1"&ECHO. L Still equals 0, See "%L = 0"! - Waiting a split second &ping -n 1 127.0.0.1>NUL )
Native FOR /L Counter:
FOR /L %L IN (-2147483648,1,2147483648) DO #(
ECHO.Current value of L: %L - Waiting a split second &ping -n 1 127.0.0.1>NUL )
Counting Sets of 4294967295 and Showing Current Value of L:
FOR /L %L IN (1,1,2147483648) DO #(
(
IF %L EQU 0 SET /A "#+=1">NUL
)&SET /A "#+=0"&ECHO. Sets of 4294967295 - Current value of L: %L - Waiting a split second &ping -n 1 127.0.0.1>NUL )
However, what if:
You're interested in reviewing the output of the monitor, and concerned that I will pass the 9999 line buffer limit before I check it after it has completed.
You'd like to take some additional actions once the Thing I am monitoring is finished.
For this, I determined how to use a couple methods to break the FOR Loop prematurely effectively turning it into a "DO WHILE" or "DO UNTIL" Loop, which is otherwise sorely lacking in CMD.
NOTE: Most of the time a loop will continue to iterate past the condition you checked for, often this is a wanted behavior, but not in our case.
Turn the "infinite Loop" into a "DO WHILE" / "DO UNTIL" Loop
UPDATE: Due to wanting to use this code in CMD Scripts (and have them persist!) as well as CLI, and on thinking if there might be a "more Correct" method to achieve this I recommend using the New method!
New Method (Can be used inside CMD Scripts without exiting the script):
FOR /F %%A IN ('
CMD /C "FOR /L %%L IN (0,1,2147483648) DO #( ECHO.%%L & IF /I %%L EQU 10 ( exit /b ) )"
') DO #(
ECHO %%~A
)
At CLI:
FOR /F %A IN ('
CMD /C "FOR /L %L IN (0,1,2147483648) DO #( ECHO.%L & IF /I %L EQU 10 ( exit /b ) )"
') DO #(
ECHO %~A
)
Original Method (Will work on CLI just fine, but will kill a script.)
FOR /L %L IN (0,1,2147483648) DO #(
ECHO.Current value of L: %L - Waiting a split second &ping -n 1 127.0.0.1>NUL&(
IF /I %L EQU 10 (
ECHO.Breaking the Loop! Because We have matched the condition!&DIR >&0
)
)
) 2>NUL
Older Post Stuff
Through chance I had hit upon some ways to exit loops prematurely that did not close the CMD prompt when trying to do other things which gave me this Idea.
NOTE:
While ECHO.>&3 >NUL had worked for me in some scenarios, I have played with this off and on over the years and found that DIR >&0 >NUL was much more consistent.
I am re-writing this answer from here forward to use that method instead as I recently found the old note to myself to use this method instead.
Edited Post with better Method Follows:
DIR >&0 >NUL
The >NUL is optional, I just prefer not to have it output the error.
I prefer to match inLine when possible, as you can see in this sanitized example of a Command I use to monitor LUN Migrations on our VNX.
for /l %L IN (0,0,1) DO #(
ECHO.& ECHO.===========================================& (
[VNX CMD] | FINDSTR /R /C:"Source LU Name" /C:"State:" /C:"Time " || DIR >&0 >NUL
) & Ping -n 10 1.1.1.1 -w 1000>NUL )
Also, I have another method I found in that note to myself which I just re-tested to confirm works just as well at the CLI as the other method.
Apparently, when I first posted here I posted an older iteration I was playing with instead of the two newer ones which work better:
In this method, we use EXIT /B to exit the For Loop, but we don't want to exit the CLI so we wrap it in a CMD session:
FOR /F %A IN ('CMD /C "FOR /L %L IN (0,1,10000000) DO #( ECHO.%L & IF /I %L EQU 10 ( exit /b ) )" ') DO #(ECHO %~A)
Because the loop itself happens in the CMD session, we can use EXIT /B to exit the iteration of the loop without losing our CMD Session, and without waiting for the loop to complete, much the same as with the other method.
I would go so far as to say that this method is likely the "intended" method for the sort of scenario where you want to break a for loop at the CLI, as using CMD session is also the only way to get Delayed expansion working at the CLI for your loops, and the behavior and such behavior is clearly an intended workflow to leave a CMD session.
IE: Microsoft clearly made an intentional effort to have CMD Exit /B For loops behave this way, while the "Intended" way of doing this, as my other method, relies on having accidentally created just the right error to kick you out of the loop without letting the loop finish processing, which I only happenstantially discovered, and seems to only reliably work when using the DIR command which is fairly strange.
So that said, I think it's probably a better practice to use Method 2:
FOR /F %A IN ('CMD /C "FOR /L %L IN (0,1,10000000) DO #( ECHO.%L & IF /I %L EQU 10 ( exit /b ) )" ') DO #(ECHO %~A)
Although I suspect Method 1 is going to be slightly faster:
FOR /L %L IN (0,1,10000000) DO #( ECHO.%L & IF /I %L EQU 10 ( DIR >&) >NUL ) )
And in either case, both should allow DO-While loops as you need for your purposes.
#echo off
set countfiles=10
:loop
set /a countfiles -= 1
echo hi
if %countfiles% GTR 0 goto loop
pause
on the first "set countfiles" the 10 you see is the amount it will loop
the echo hi is the thing you want to loop
...i'm 5 years late
It was very useful for me i have used in the following way to add user in active directory:
:: This file is used to automatically add list of user to activedirectory
:: First ask for username,pwd,dc details and run in loop
:: dsadd user cn=jai,cn=users,dc=mandrac,dc=com -pwd `1q`1q`1q`1q
#echo off
setlocal enableextensions enabledelayedexpansion
set /a "x = 1"
set /p lent="Enter how many Users you want to create : "
set /p Uname="Enter the user name which will be rotated with number ex:ram then ram1 ..etc : "
set /p DcName="Enter the DC name ex:mandrac : "
set /p Paswd="Enter the password you want to give to all the users : "
cls
:while1
if %x% leq %lent% (
dsadd user cn=%Uname%%x%,cn=users,dc=%DcName%,dc=com -pwd %Paswd%
echo User %Uname%%x% with DC %DcName% is created
set /a "x = x + 1"
goto :while1
)
endlocal

Resources