Sifting Files by Date
I've seen several similar inquiries in alt.msdos.batch recently to the one show below.
  The Challenge

Newsgroups: alt.msdos.batch Subject: Rename File to Current Date/Time Batfan wrote: I've seen numerous WinNT postings on this subject and have yet to find anything that will work in Windows 98 for me. What I'm looking to do is to rename a file that's already in a yyyymm.log format to a mmddyyyy.log format instead (using the current date and time). I'd also like to delete old mmddyyyy.log files after they are more than 10 days old (which shouldn't be too hard after a check for the current mmddyyyy is implemented, then subtract each files dd number and check to see if there is a difference of more than 10. Anyway, if you can offer any help on this, please email me (remove NOSPAM from email). Again, I need this for WIN98! And btw, I do program in Pascal and C++, but I've only done limited batch file programming. Would love to learn each step of the process if you'd be willing to comment the code. Thanks, Wayne
I started a response to this particular post, but realized just how immense a problem this is to handle in batch. Poor Wayne really had no idea what he was asking. He has obviously never tried to perform any math in a batch procedure or he'd know just how hard it is to "check to see if there is a difference of more than 10". Especially, when it comes to doing that with a date string!

Well, I decided to accept the challenge and this new article is the result. It actually covers two subjects: 1. Making a date derived file name and sifting a file listing based on a cuttoff date. Note that this is NOT intended for use with NT.

BTW, folks, I don't do courtesy Email responses to anti-spam addressees. I spend too much time answering questions as it is. I'm just not going to take the time to patch up an adulterated address. Don't get me wrong, I sympathize with you're plight, but I just can't deal.

  One More Date Parsing Routine
First, here's my latest code for parsing the current date on a DOS/Win 95/98 equipped machine ...
:: UniDateC.bat - A way to parse and store today's date into the environment :: Tom lavedas <lavedas@pressroom.com> :: http://www.pressroom.com/~tglbatch @%4 echo %dbgu% off if '%1'=='' %0 Date > {A} % Default 'object' name % :: Setup to get current date into temporary file echo @prompt if '%%%%2==' %%%%0=$D$_echo %%%%2y$g{C}.bat$_> {A}.bat %comspec% /e:2048 /c {A}.bat > {B}.bat :: Put date into temp file {C}.bat for %%v in (call del) do %%v {B}.bat :: Test for order of MM & DD echo. | date 1/13/99 | find "(" > nul if errorlevel 1 %0 %1.DD %1.MM %1.YYYY goto:1st %0 %1.MM %1.DD %1.YYYY goto:1st :1st - Reentry with Date.MM & Date.DD in proper order on command line :: Setup for date parsing set _T= for %%v in (0 1 2 3 4 5 6 7 8 9) do set (Y/N)?%%v=%%v for %%v in (/ // -) do set (Y/N)?%%v=; echo if %%2"==(Y/N)?y" exit > {A}.bat >>{A}.bat echo %%"%%>>%%"%%{B}.bat echo set _T=%%%%_T%%%%%%%%%%2%%%% :: This peculiar line of code parses date del {A} /p < {C}.bat | %comspec% /e:2048 /k > nul :: Reassembles the date characters into variables for %%v in ({B}.bat del) do call %%v {?}.bat %0 %1 %2 %3 goto:2nd %_T% :2nd - Reentry to complete process and store result into var's date %5/%6/%7 set %1=%5 set %2=%6 set %3=%7 for %%v in (0 1 2 3 4 5 6 7 8 9 / // -) do set (Y/N)?%%v= set _T= :End
If UnidateC.bat is called without any arguments, it returns the parsed date in three default (object like) variables: Date.MM, Date.DD, Date.YYYY. These can then be used to rename the file, something like ... call unidateC ren %Date.MM%%Date.YYYY%.log %Date.MM%%Date.DD%%Date.YYYY%.* Or the routine can be called with one argument to name the 'object' (e.g. call unidateC Obj => Obj.MM, Obj.DD, Obj.YYYY).

The routine should work regardless of country language or date format, though I have not been able to test it in ALL environments ;-)

To get a glimmer of an idea of how the procedure works, try typing the following at a command prompt and reviewing the result ...

rem > {A} echo 08/11/1999Y| del {A} /p The procedure uses the output of a similar DEL statement to do the parsing and two temporary batch procedures, {A}.BAT and {B}.bat, to collect the parsed pieces.
  Step One: Manipulating Dates
Now if you think UniDateC.bat is hard to understand, I better warn you that the other part of the job is just as hard, or harder.

The approach I recently developed uses a 'flag' batch file which has two lines in it ...

set RemoveDate=08/09/1999 set Yesterday=08/10/1999 This batch file needs to be constructed once, manually. After it's creation, the cleanup process incements each date until the 'Yesterday' variable equals the current date, using the following routine ...
:: IncrDate.bat - A routine to advance a date by one day. :: Tom lavedas <lavedas@pressroom.com> :: http://www.pressroom.com/~tglbatch @echo %dbgi% off if '%1==' %0 Tomorrow if not '%Today%==' goto Skip echo @prompt if '%%%%2==' %%%%0 $D$_set Today=%%%%2$_ > %temp%.\~tempA.bat %comspec%/e:2048/c %temp%.\~tempA.bat > %temp%.\~tempB.bat for %%v in (%temp%.\~tempB.bat del) do call %%v %temp%.\~temp?.bat :Skip if %1==Tomorrow set Tomorrow=%Today% set _T=date %%%1%%$_time %%%%1$_ set _T=%_T%echo.$Bdate$g%temp%.\~tempA.bat$_time $T$_ echo @prompt %_T% > %temp%.\~tempA.bat echo.|%comspec%/e:2048/c %temp%.\~tempA.bat | find/v "(" > %temp%.\~tempB.bat if not errorlevel 1 echo *** Invalid date in variable %1 *** if not errorlevel 1 goto Error Abort (an intentionally undefined label) call %temp%.\~tempB.bat 23:59:59.99 date %Today% echo set %1=%%4> current.bat for %%v in (%temp%.\~tempA.bat del) do call %%v %temp%.\~temp?.bat del current.bat
IncrDate takes one optional argument: a string NAMING the variable which contains the date to be incremented (not the date string). If no name is given, today's date is incremented by one day and stored in the variable 'Tomorrow'. Note that IncrDate.bat IS country language dependent. It assumes the output of the DATE function begins with the word 'Current' and that the first line of output has exactly four words preceeding the date string. The temporary batch procedure, Current.bat, must be adapted accordingly to accommodate other language versions of DOS/Windows.

Beside this limitation, IncrDate, manipulates the system clock, causing it to run a tiny bit slower for each call. This shouldn't be too much of a problem as PC clocks are not known for their accuarcy, anyway.

There are still two more procedures necessary to fulfill the requirement of deleting files based on date: a procedure to oversee the whole process and a date search procedure. I think I'll cover the oversight procedure before getting to the date search . It's a bit simpler.

  Step Two: The Cleanup Engine
The oversight routine, shown below, reads the current date, the dates in the flag file, calls IncrDate.bat to adjust the dates and calls the file search procedure to act on the files created before the appropriate date.
:: Cleanup.bat - Locate files X days old as defined by the date flag
:: file, C:\_dates_.bat.
:: Requires IncrDate.bat and DSearch.Bat. :: Tom lavedas <lavedas@pressroom.com> :: http://www.pressroom.com/~tglbatch @echo %dbgc% off if '%1==' %0 .\ for %%v in (%1 %1.\nul) do if exist %%v goto Okay echo Folder %1 not found goto End :Okay if not exist c:\_dates_.bat goto End call c:\_dates_.bat echo @prompt if '%%%%2==' %%%%0 $D$_set Today=%%%%2$_ > %temp%.\~tempA.bat %comspec%/e:2048/c %temp%.\~tempA.bat > %temp%.\~tempB.bat for %%v in (%temp%.\~tempB.bat del) do call %%v %temp%.\~temp?.bat if %Yesterday%==%Today% echo Cleanup process already run today (%Today%). if %Yesterday%==%Today% goto End if not %1==.\ %1.\ if exist %1 cd %1\.. if exist %1.\nul cd %1. :Loop for %%v in (RemoveDate Yesterday) do call IncrDate %%v if not %Yesterday%==%Today% goto Loop echo echo del %%1 > %temp%.\~process.bat call DSearch %RemoveDate% %temp%.\~process.bat del %temp%.\~process.bat echo set Yesterday=%Yesterday%> c:\_dates_.bat echo set RemoveDate=%RemoveDate%>>c:\_dates_.bat :End
Cleanup.bat takes one optional argument, the name of a folder/directory to sift. Note that the flag file, c:\_dates_.bat MUST exist before executing Cleanup.bat. It will be updated automatically upon completion of the Cleanup process using the IncrDate.bat procedure. The updating of the variables takes place before processing the files, but the '_date_.bat' file is rewritten after the process is completed. This acts as a flag for completion of the process. Subsequent calls to Cleanup on the same day results merely in a message declaring that the cleanup has already been run. Note that the date incrementing loop is repeated until "Yesterday's" date becomes "Today's" date.

A simple string comparison is used with no error checking. This is sufficient as long as care is taken in constructing the '_dates_.bat' file the first time to insure that the date formats are valid and that "Yesterday's" date is equal to or less than the current date at the time it is first run. IncrDate has error checking code to check for valid date formats, but there is no check that Yesterday's date is less than or equal to Today's before the procedure runs. If a date later than the systems currently set date is written into _dates_.bat, the Cleanup procedure will enter an infinite loop.

  Step Three: Sift for Out-of-date Files
Finally, the routine that does the actual sifting of files, based on the criterian date.
:: Desearch.bat - Processes files created before given date,
:: using procedure named on command line.
:: Tom lavedas <lavedas@pressroom.com> :: http://www.pressroom.com/~tglbatch @%3 echo %dbgd% off if '%1==' goto End Nothing to do. if '%2==' %0 %1 %temp%.\~process.bat % Default process name % :; Get today's date echo @prompt if '%%%%2==' %%%%0=$D$_set _D=%%%%2$_ > %temp%.\{A}.bat %comspec% /e:2048 /c %temp%.\{A}.bat > %temp%.\{B}.bat for %%v in (%temp%.\{B}.bat del) do call %%v %temp%.\{?}.bat :: Change to cut-off date entered and test validity . echo. | date %1 | find "(" if not errorlevel 1 goto End Invalid date rem Create marker file dated on cut-off date> {1} date %_D% % Restore today's date % set _D= :: Create DEBUG script of sorted directory listing. echo a> %temp%.\{1} for %%v in (/) do dir /a-d/b/od/-p/z >> %temp%.\{1} for %%v in (/6) do if %%v==6 dir /a-d/b/od/-p >> %temp%.\{1} del {1} for %%v in (. .q) do echo%%v >> %temp%.\{1} echo prompt call %0 %1 %2 goto:2nd > %temp%.\{2} :: Run script debug < %temp%.\{1} | find ":" >> %temp%.\{2} echo exit>>%temp%.\{2} :: Process modified directory listing %dbgd% ctty nul %comspec%/e:2048/k < %temp%.\{2} |find ":0" | %comspec%/k %dbgd% ctty con for %%v in (%temp%.\{2}.bat del goto:End) do call %%v %temp%.\{?} :2nd - Reentry point with file names on command line. for %%v in (' '{1}) do if '%5==%%v exit at cut-off date echo %2 %5 > %temp%.\{2}.bat % Run Process on file % :End
DSearch.bat works by adjusting the system date to the value stored in the environment as RemoveDate, writes a flag file, {1}, in the current folder, restores the date to Today's date, creates a file listing sorted by date and processes that list until the name of the flag file (or the end of the list) is reached.

DSearch takes one optional argument: the name of a process, program or command. It runs that process once for each file name on the list.

What's left to do? Put all of the procedures in a directory/folder named in the PATH and add a call to the Cleanup.bat procedure in your AUTOEXEC.BAT (DOS/Win 95/98) or in the "C:\Windows\All Users\Start Menu\Startup" folder (Win 95/98). Or, call it manually when desired.

Well, I said the solution was immense, didn't I? I'll try to tweak the procedures and expand the explanation of their function over the next days. It might be worth a check back here sometime, if you're interested, but I can't guarantee anything.




Top | General Notes | Homepage