Windows Bat Del old files - batch-file

i have writing a window batch to del the files order than 3 months in some directory, however, there are some problem on :Loop_Folder_Del_Old_Files. I don't know why i cannot assign the last modified date of the files, please help to find out the problem. Below are the source code and program result, thanks!
Source Code
:program_start
#ECHO ON
echo program_start
:Parameter_Settings
set filePath=c:\New Folder
set delPeriod=3
echo %date%
set curYYYY=%date:~10,4%
set curMM=%date:~7,2%
set curDD=%date:~4,2%
:Set_The_Date_Of_3_Months_Ago
set /A curMM=curMM - %delPeriod%
if "%curMM%" LEQ "0" (
set /A curMM="(curMM + 12 - %delPeriod%)%%12"
if %curMM% == 0 (set curMM=12) ELSE (set curMM=%curMM%)
set /A curYYYY=curYYYY - 1
)
set curMM=00%curMM%
set curMM=%curMM:~-2%
set curDate=%curYYYY%%curMM%%curDD%
:Loop_Folder_Del_Old_Files
for %%a IN ("%filePath%\*.*") DO (
set ltdate=%%~ta
set fileDate=%ltdate:~6,4%%ltdate:~3,2%%ltdate:~0,2%
if "%fileDate%" LSS "%curDate%" Del /Q "%%a"
)
:end
echo program end
pause
Result
C:\>echo program_start
program_start
C:\>set filePath=c:\New Folder
C:\>set delPeriod=3
C:\>echo Fri 22/11/2013
Fri 22/11/2013
C:\>set curYYYY=2013
C:\>set curMM=11
C:\>set curDD=22
C:\>set /A curMM=curMM - 3
C:\>if "8" LEQ "0" (
set /A curMM="(curMM + 12 - 3)%12"
if 8 == 0 (set curMM=12 ) ELSE (set curMM=8 )
set /A curYYYY=curYYYY - 1
)
C:\>set curMM=008
C:\>set curMM=08
C:\>set curDate=20130822
C:\>for %a IN ("c:\New Folder*.*") DO (
set ltdate=%~ta
set fileDate=~6,4%ltdate:~3,2%ltdate:~0,2
if "" LSS "20130822" Del /Q "%a"
)
C:\>(
set ltdate=22/11/2013 05:36 PM
set fileDate=~6,4%ltdate:~3,2%ltdate:~0,2
if "" LSS "20130822" Del /Q "c:\New Folder\New Text Document (2).txt"
)
C:\>(
set ltdate=22/11/2013 05:36 PM
set fileDate=~6,4%ltdate:~3,2%ltdate:~0,2
if "" LSS "20130822" Del /Q "c:\New Folder\New Text Document.txt"
)
C:\>echo program end
program end
C:\>pause
Press any key to continue . . .

maybe the use of forfiles to delete old files could be better in handle?

You need to enable delayed expansion to make your variables work inside the for loop.
Add
setlocal enabledelayedexpansion
at the top of your bat file
and then, refer to the variable using the !var! syntax (instead of %var%)
if "!fileDate!" LSS "!curDate!" Del /Q "%%a"

Further to PA's correct response,
set /A curMM=curMM - %delPeriod%
if "%curMM%" LEQ "0" (
set /A curMM="(curMM + 12 - %delPeriod%)%%12"
if %curMM% == 0 (set curMM=12) ELSE (set curMM=%curMM%)
set /A curYYYY=curYYYY - 1
)
Is unreliable as currMM may be 08 or 09 which would generate a syntax-error as batch assumes that any numeric starting with 0 is OCTAL.
set /A curMM=1%curMM% - 100 - %delPeriod%
if %curMM% LEQ 0 (
set /A curMM=curMM + 12
set /A curYYYY=curYYYY - 1
)
is better. The comparison is executed against the NUMERIC value, not a string value. The value of curMM would be 0 for Dec, -1 for Nov, etc - so simply adding 12 and subtracting 1 from the year is sufficient and clearer.

Related

Bat Script Loop Date Range Format Input [duplicate]

This question already has an answer here:
How to generate succesive dates? (add N days to date)
(1 answer)
Closed last month.
I have bat script for loop function
#echo off set /p a="Start Date(yyyymmdd):"%=% set /p b="Finish Date
(yyyymmdd):"%=%
echo %a% echo %b%
echo start loop
FOR /L %%c IN (%a%,1,%b%) DO ( echo %%c
set inputan=%%c
call test_loopong.bat %inputan%
)
I need to run according to the range of start and end date input.
But from the script above, for example the start date is 20221231 and the end date is 20230103
will loop all numbers from 20221231, 20221232, 20221233......20230103
Please someone can help me.
Thanks
#ECHO Off
SETLOCAL
set /p "startdate=Start Date(yyyymmdd):"
set /p "finishdate=Finish Date (yyyymmdd):"
echo %startdate%
echo %finishdate%
IF %finishdate% lss %startdate% ECHO Invalid DATE range&GOTO :eof
echo start loop
FOR /L %%c IN (%startdate%,1,%finishdate%) DO (
FOR /F %%y IN ('set /a inputan^=%%c %% 100') DO IF %%y lss 32 IF %%y gtr 0 (
echo %%c
set /a inputan=%%c
call :test_loopong.bat %%c
)
)
GOTO :EOF
:test_loopong.bat
ECHO IN test_loopong : %%1=%1 inputan=%inputan%
GOTO :eof
I changed the variable names so that they are maintainable, 'though I've no idea what inputan means.
Added invalid date-range check.
Note syntax of set /p
Forget for the moment what for...%%y... does.
I used a set /a since %%c must be numeric.
Use set "var1=value" for setting STRING values - this avoids problems caused by trailing spaces.
I converted test_loopong.bat to an internal subroutine (call :name) for demonstration purposes to avoid having to generate another file.
I'm having that subroutine simply display the value of the variable inputan and the parameter %1. %%c can be delivered as a parameter to the subroutine (be it internal or external) but your syntax would appear to omit the parameter because of the delayed expansion trap - %var% will be replaced by the value of var at the time the outer loop (for...%%c) is encountered.
Now the for...%%y jiggery-pokery.
The command set /a inputan=%%c % 100 would set inputan to %%c mod 100. There's no importance about the variable name - that one's about to be assigned a different value again in a couple of lines.
When used in a for /f, batch will execute the set/a command and echo the result to the metavariable %%y.
However, = and % are special characters and need to be "escaped" (interpreted without their special meaning). Th escape character for most specials is caret (^) but for % is % itself.
The result of the calculation is assigned to %%y and we can then test that %%y is lss 32 - Less than 32, and also it is gtr 0 - Greater than 0. Only then do we call test_loopong.bat. This eliminates most non-dates
If you don't want to skip the non-dates, then remove the for...%%y line and delete one of the ) lines.
--- further thoughts ----
Suppressing the days 32..99 & 00 really only does half the job. Much better if we suppress months 13..99 & 00. The revision then would be (presenting just the main loop, minus the frippery)
FOR /L %%c IN (%startdate%,1,%finishdate%) DO (
FOR /F %%e IN ('set /a inputan^=%%c %% 10000') DO IF %%e lss 1232 IF %%e gtr 100 (
FOR /F %%y IN ('set /a inputan^=%%c %% 100') DO IF %%y lss 32 IF %%y gtr 0 (
echo %%c
set /a inputan=%%c
call :test_loopong.bat %%c
)
)
)
Same principle, just dealing with months in place of days.
One slight problem with this method is that it's as slow as a wet week.
So - a different approach
SET /a yyyy=%startdate:~0,4%
SET /a mm1=1%startdate:~4,2%
SET /a dd1=1%startdate:~-2%
:loop2
SET /a inputan=%yyyy%0000+%mm1%00+dd1-10100
IF %inputan% gtr %finishdate% GOTO :eof
CALL :test_loopong.bat %inputan%
SET /a dd1+=1
IF %dd1% leq 131 GOTO loop2
SET /a dd1=101&SET /a mm1+=1
IF %mm1% leq 112 GOTO loop2
SET /a mm1=101
SET /a yyyy+=1
GOTO loop2
This sets yyyy to the year and mm1/dd1 to 100+(month/day). 100+ needs to be done since batch regards a numeric string that starts 0 as
octal hence August and September cause problems.
So - calculate inputan by tringing 4 0s to yyyy, adding 100*mm1 and dd1, then subtracting 10100 since mm1 is mm+100 and dd1 is dd+100.
If the resultant inputan is greater than the finish date, end the routine.
Test using inputan.
Next day - add 1 to dd1.
If the result is less than or equal to 131, we're fine.
Otherwise set dd1 to 101 and increment the month
Same recipe for mm1, limit is 112 and bump the year if required.
---- Further revision to deal with non-dates 31st Apr, Jun, Sep, Nov and Feb (include leap years) ---
#ECHO Off
SETLOCAL
set /p "startdate=Start Date(yyyymmdd):"
set /p "finishdate=Finish Date (yyyymmdd):"
echo %startdate%
echo %finishdate%
IF %finishdate% lss %startdate% ECHO Invalid DATE range&GOTO :eof
echo start loop
GOTO ver2
FOR /L %%c IN (%startdate%,1,%finishdate%) DO (
FOR /F %%e IN ('set /a inputan^=%%c %% 10000') DO IF %%e lss 1232 IF %%e gtr 100 (
FOR /F %%y IN ('set /a inputan^=%%c %% 100') DO IF %%y lss 32 IF %%y gtr 0 (
echo %%c
set /a inputan=%%c
call :test_loopong.bat %%c
)
)
)
GOTO :EOF
:ver2
SET /a yyyy=%startdate:~0,4%
SET /a mm1=1%startdate:~4,2%
SET /a dd1=1%startdate:~-2%
:loop2
SET /a inputan=%yyyy%0000+%mm1%00+dd1-10100
IF %inputan% gtr %finishdate% GOTO :eof
CALL :test_loopong.bat %inputan%
SET /a dd1+=1
SET /a inputan=yyyy %% 4
IF %mm1%==102 IF %dd1% gtr 129 (GOTO nextmonth) ELSE IF %inputan% neq 0 IF %dd1%==129 GOTO nextmonth
FOR %%e IN (104 106 109 111) DO IF %%e131==%mm1%%dd1% GOTO nextmonth
IF %dd1% leq 131 GOTO loop2
:nextmonth
SET /a dd1=101&SET /a mm1+=1
IF %mm1% leq 112 GOTO loop2
SET /a mm1=101
SET /a yyyy+=1
GOTO loop2
GOTO :eof
:test_loopong.bat
ECHO IN test_loopong : %%1=%1 inputan=%inputan%
GOTO :eof

Nested If Statements and Command RD

I'm working with a nested if statement to delete directories if they meet a certain criteria. This is part of a for loop to check all directories in a given location. Variable %JDcalc% represents the difference between today's julian day and the directory's julian day. Variable %fdate% is the directory name. The errorlevel is deteremined by the following:
echo.%fdate% | findstr /C:"OE"
This nested if statement works:
if %errorlevel% EQU 0 (if %JDcalc% GEQ 31 (echo DELETE) else (echo KEEP)) else (if %JDcalc% GEQ 14 echo DELETE)
As soon as I switch echo DELETE to rd %fdate% the batch script outputs, "The system cannot find the specified file."
if %errorlevel% EQU 0 (if %JDcalc% GEQ 31 (rd %fdate%) else (echo KEEP)) else (if %JDcalc% GEQ 14 rd %fdate%)
If I use a simpler if statement like if %JDcalc% GEQ 14 (rd %fdate%) it works fine.
EDIT: Below is the script in question.
#echo off
cd /d C:\Batch\Maximus\Dailies
:TODAYS_JULIAN_DAY
set /a JD=2458939
::a calculation that determines this number is usually here
::above is the julian day for 3.30.2020
:FOR_LOOP_FOLDER_NAME
for /f "delims=" %%a in ('dir /a:d /b') do (
SETLOCAL EnableDelayedExpansion
set fdate=%%a
::fdate is set to the date in the directory name
::with a naming convention of yymmdd
call :SUBROUTINE
ENDLOCAL
)
GOTO END_BATCH
:SUBROUTINE
set folyy=%fdate:~0,2%
set folmm=%fdate:~2,2%
set foldd=%fdate:~4,2%
set /a "folyy=1000020%folyy% %%10000,folmm=100%folmm% %% 100,foldd=100%foldd% %% 100"
set /a folJD=foldd-32075+1461*(folyy+4800+(folmm-14)/12)/4+367*(folmm-2-(folmm-14)/12*12)/12-3*((folyy+4900+(folmm-14)/12)/100)/4
set /a JDcalc=JD-folJD
echo.%fdate% | findstr /C:"OE"
if %errorlevel% EQU 0 (if %JDcalc% GEQ 31 (rd %fdate%) else (echo KEEP)) else (if %JDcalc% GEQ 14 rd %fdate%)
GOTO :eof
:END_BATCH
ENDLOCAL

(Use a batch file) Get the filename only into a For loop

This is the new version according to your comments.
It still doesn't work.
I try to rename files like
2006 Gaspésie - Croisière à la baleine 1023.jpg
2006 Gaspésie - Croisière baleine 28.jpg
To
2006-06 Croisière baleine 001.jpg
2006-06 Croisière baleine 002.jpg
The problem is that the fields F1 and F2 are empty when I do echo and when I do rename.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set /p "NewFileName=New name file (without the extension): "
set /p "LenghtMask=Mask lenght of the number: "
set /a FileNo=0
for %%I in (*.JPG) do (
echo.
#echo off
set /a FileNo=!FileNo!+1
call :return_no_mask !FileNo! %LenghtMask% FileNoMask
#echo on
set "F1=%%~I"
echo F1 %F1%
set ExtensionName=%%~xI
set "F2=%NewFileName% %FileNoMask%%ExtensionName%"
echo F2 %F2%
rename "%F1%" "%F2%"
pause
)
goto:EOF
::--------------------------------------------------
::Return the number (fill with 0 to have the right lenght)
:: in Number to use
:: in Mask's lenght ex: 2
:: out Number
::--------------------------------------------------
:return_no_mask
set NoIn=%1
::---Search the lenght of the number
set /a LenghtNo=1
if "%NoIn%" GEQ "10" set /a LenghtNo=2
if "%NoIn%" GEQ "100" set /a LenghtNo=3
if "%NoIn%" GEQ "1000" set /a LenghtNo=4
if "%NoIn%" GEQ "10000" set /a LenghtNo=5
::---Fill with zero
set NoOut=
:test_add
if "%LenghtNo%" LSS "%2" (
set NoOut=0%NoOut%
set /a LenghtNo+=1
goto :test_add
)
set NoOut=%NoOut%%1
set %3=%NoOut%
EXIT /B 0
::--------------------------------------------------
::End of file
::--------------------------------------------------
:EOF
endlocal
Thank you

Batch file run cmd1 if time 10pm-4am else run cmd2

I have a batch file and within that batch file I need to run one of two commands depending on time of my server.
If the time is between 22:00:00 and 03:30:00 -- xcopy /Y a\1.txt c\1.txt
If the time is before or after this range -- -- xcopy /Y b\1.txt c\1.txt
This will use xcopy to switch a file back and forth depending on the time.
I know this is easy but my brain just won't work atm lol
Edit:
Went with 22:00 and 4:00... this is what I came up with but it doesn't seem like the best way...
set current_time=%time:~0,5%
if "%current_time%" lss "22:00" goto daycycle
if "%current_time%" gtr " 4:00" goto daycycle
echo Do this between 10pm and 4am
goto continue
:daycycle
echo Do this before 10pm and after 4am
:continue
#echo off
setlocal enableextensions disabledelayedexpansion
set "now=%time: =0%"
set "task=day"
if "%now%" lss "03:30:00,00" ( set "task=night" )
if "%now%" geq "22:00:00,00" ( set "task=night" )
call :task_%task%
endlocal
exit /b
:task_day
:: do daily task
goto :eof
:task_night
:: do nightly task
goto :eof
EDITED - The previous code should work under the conditions in the original question. But will fail in different time configurations. This should solve the usual problems
#echo off
setlocal enableextensions disabledelayedexpansion
call :getTime now
set "task=day"
if "%now%" lss "03:30:00,00" ( set "task=night" )
if "%now%" geq "22:00:00,00" ( set "task=night" )
call :task_%task%
echo %now%
endlocal
exit /b
:task_day
:: do daily task
goto :eof
:task_night
:: do nightly task
goto :eof
:: getTime
:: This routine returns the current (or passed as argument) time
:: in the form hh:mm:ss,cc in 24h format, with two digits in each
:: of the segments, 0 prefixed where needed.
:getTime returnVar [time]
setlocal enableextensions disabledelayedexpansion
:: Retrieve parameters if present. Else take current time
if "%~2"=="" ( set "t=%time%" ) else ( set "t=%~2" )
:: Test if time contains "correct" (usual) data. Else try something else
echo(%t%|findstr /i /r /x /c:"[0-9:,.apm -]*" >nul || (
set "t="
for /f "tokens=2" %%a in ('2^>nul robocopy "|" . /njh') do (
if not defined t set "t=%%a,00"
)
rem If we do not have a valid time string, leave
if not defined t exit /b
)
:: Check if 24h time adjust is needed
if not "%t:pm=%"=="%t%" (set "p=12" ) else (set "p=0")
:: Separate the elements of the time string
for /f "tokens=1-5 delims=:.,-PpAaMm " %%a in ("%t%") do (
set "h=%%a" & set "m=00%%b" & set "s=00%%c" & set "c=00%%d"
)
:: Adjust the hour part of the time string
set /a "h=100%h%+%p%"
:: Clean up and return the new time string
endlocal & if not "%~1"=="" set "%~1=%h:~-2%:%m:~-2%:%s:~-2%,%c:~-2%" & exit /b
Try this:
#echo off
set hour=%time:~0,2%
set min=%time:~3,2%
if %hour% GEQ 22 (
xcopy /Y a\1.txt c\1.txt
) ELSE (
if %hour% LEQ 3 (
if %hour% EQU 3 if %min% GTR 30 (
xcopy /Y b\1.txt c\1.txt
goto :END
)
xcopy /Y a\1.txt c\1.txt
) ELSE (
xcopy /Y b\1.txt c\1.txt
goto :END
)
)
:END
And I'm rather sure that will do what you want. Manual if statements for the win! Note it would be a lot easier if it was 22:00 to 4:00 or 3:00. I had to incorperate the :30 minute checker.
But yea, it might not work, so just check it before you put it up on your server.
Monacraft.
What should work:
#echo off
:loop
set hour=%time:~0,2%
set min=%time:~3,2%
cls
ECHO %hour%
ECHO %min%
ECHO This %I%
IF %hour% == 14 GOTO Test2
goto loop
:Test2
IF %min% == 58 GOTO YUP
IF %min% == 59 GOTO LATE
Goto loop
:YUP
SET I=0
GOTO loop
:LATE
SET I=NOPE
GOTO loop

batch file : checking calendar

How I can check the date using a calender , what I want to do is that a user insert a date in this way 2012/07/15 , and i want to check if this date exist in the calender .
for example if he insert 2012/14/13 or even 2012/02/31 , i tried to do that manually but its really hard , so my qustions are :
is there a native function(or even 3d party tools) to check date in calender ?
if the user insert date and hour , I want to give same result with different time zone places ?
There is no simple native batch command that will validate your string.
Here is a batch subroutine that can be used to validate the date string.
#echo off
setlocal
set "dt="
set /p "dt=Enter a date in yyyy/mm/dd format: "
if not defined dt exit /b
call :validateDate dt && (echo date is valid) || (echo ERROR: date is invalid)
exit /b
:validateDate
setlocal enableDelayedExpansion
echo !%~1!|findstr /rx "[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]" >nul || exit /b 1
for /f "tokens=1,2,3 delims=/" %%A in ("!%~1!") do (
set /a "yyyy=10000%%A%%10000, mm=100%%B%%100, dd=100%%C%%100" || exit /b 1
)
if %mm% lss 1 exit /b 1
if %mm% gtr 12 exit /b 1
if %dd% lss 1 exit /b 1
set "months=;1-31;2-28;3-31;4-30;5-31;6-30;7-31;8-31;9-30;10-31;11-30;12-31;"
for /f "delims=;" %%A in ("!months:*;%mm%-=!") do set maxDays=%%A
set /a "divBy4=yyyy%%4, divBy100=yyyy%%100, divBy400=yyyy%%400"
if %mm% equ 2 if %divBy4% equ 0 (
if %divBy100% neq 0 set /a maxDays+=1
if %divBy400% equ 0 set /a maxDays+=1
)
if %dd% gtr %maxDays% exit /b 1
exit /b 0
It will only accept 4 digit years, 2 digit months, and 2 digit days as you have posted in your question. If you want to support 1 digit months or days, then change the FINDSTR to the following:
echo !%~1!|findstr /rx /c:"[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]" ^
/c:"[0-9][0-9][0-9][0-9]/[0-9]/[0-9][0-9]" ^
/c:"[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9]" ^
/c:"[0-9][0-9][0-9][0-9]/[0-9]/[0-9]">nul || exit /b 1
I don't understand what you want to do with the date and time zones.
The subroutine below is comprised of two parts. First it check the date given in 1st parameter and return ERRORLEVEL=1 if the date is bad. You may insert a year valid range if you wish (otherwise, remove the yyOK parts).
Then it takes a base hour and time-zone adjust in 2nd and 3rd parameters and show the adjusted date.
:validateDate result= YYYY/MM/DD [HH HHAdjust]
setlocal EnableDelayedExpansion
set i=0
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
set /A i+=1
set daysPerMonth[!i!]=%%a
)
set result=Invalid date
for /F "tokens=1-3 delims=/" %%a in ("%2") do set yy=%%a& set mm=%%b& set dd=%%c
set /A yy=%yy%, mm=10%mm% %% 100, dd=10%dd% %% 100, yyMOD4=yy%%4 >NUL 2>&1 || goto exit
if %yyMOD4% equ 0 set daysPerMonth[2]=29
set /A yyOK=0, mmOK=0, ddOK=0
rem Modify year limits as you wish (or remove they):
if %yy% geq 1900 if %yy% leq 2015 set yyOK=1
if %mm% geq 1 if %mm% leq 12 set mmOK=1
id %dd% geq 1 if %dd% leq !daysPerMonth[%mm%]! set ddOK=1
set /A dateOK=yyOK*mmOK*ddOK
if %dateOK% neq 1 goto exit
rem Date is correct
set result=%yy%/%mm%/%dd%
rem Adjust the hour to different time zone, if given
rem %3=base hour, %4=adjust with sign (+ or -)
if "%4" equ "" goto exit
set /A newHH=10%3 %% 100 %4
if %newHH% gtr 23 (
rem Pass to next day
set /A newHH-=24, dd+=1
if !dd! gtr !daysPerMonth[%mm%]! (
set /A dd=1, mm+=1
if !mm! gtr 12 (
set /A mm=1, yy+=1
)
)
)
if %newHH% lss 1 (
rem Pass to previous day
set /A newHH+=24, dd-=1
if !dd! lss 1 (
set /A mm-=1
if !mm! lss 1 (
set /A mm=12, yy-=1
)
for %%m in (!mm!) do (
set /A dd=daysPerMonth[%%m]
)
)
)
set result=%yy%/%mm%/%dd% %newHH%
:exit
endlocal & set %1=%result%
exit /B
Test this program and report any problem.
EDIT: I have modified previous program to fit new OP's requirements.
The subroutine no longer returns an ERRORLEVEL value with the result. It must be called now with a new first parameter that indicate the variable that will receive the result, that will be "Invalid date" if original date is bad. If a base and adjustment hours are given, returned value include the adjusted date and hour. For example:
call validate.bat adjusted= 2012/3/5 3 -2
if "%adjusted%" neq "Invalid date" (
echo Adjusted date and hour are: %adjusted%
)
Antonio

Resources