Printer Version Table of Contents Project Home Page
.EXAMPLE.....: 89 Delete files more than X days old (use a batch-file subroutine)
.CATEGORY....: examples
.DISCUSSION..:

Some background

I present two versions of this batch file. The older version is for use with DOS, the newer version for use with Win9X, Win2000, ME, NT, etc. The difference in the batch files is due to the fact that the newer versions of Windows (1) support long filenames, which FDATE does not, and (2) don't allow Fdate to use its /V feature to set environment variables

Introduction

This batch file uses a crude, but effective, technique for giving a batch file the ability to call subroutines. If you've never seen something like this before, it is sort of mind-blowing. Here's some commentary on the more important lines involved in the technique.

 if (%1)==(SUBROUTINE) goto %2
If the first parameter, %1, is "SUBROUTINE", then the batch file recognizes that it is being called for the purpose of executing one of its own subroutines. In such a case, it does a GOTO to the start of the requested subroutine. That is, it goes to the label whose name is in the second parameter.

Explicitly specifying the name of the desired subroutine permits permits us to have multiple subroutines in the batch file, each with its own name. (As it happens, in this batch file we have only one subroutine, named "PROCESS!")

If the first parameter is not "SUBROUTINE", then we fall through and begin executing the main routine of the batch file. In such a case, the first parameter (%1) may contain a number, indicating the number of days to use in determining which files to delete.

Note that this technique will make the batch file malfunction if the user himself ever executes the batch file from the DOS command line with the word "SUBROUTINE" as the first parameter, the word "PROCESS!" as the second parameter, and a third parameter that is missing or not a valid filename. This is so unlikely, however, that it is reasonable to assume that it will never happen.

 for %%v in (*.*) do  CALL %0 SUBROUTINE PROCESS! %%v
In a batch file, %0 contains the name by which the batch file was invoked. We use this fact to allow a batch file to call itself, regardless of what name the user has given to it.

The first parameter passed, when the batch file calls itself, is the string "SUBROUTINE". This string allows the batch file to recognize when it is being called for the purpose of executing one of its own subroutines.

The second parameter is the name of the subroutine that we want to call: in this case, "PROCESS!".

The third parameter is what we would normally think of as the first parameter to the subroutine. In this case, when the FOR statement is executed, and the substitution for %%v takes place, it will contain the name of the file to be processed. Note that we could, if we wished, pass additional parameters to the subroutine. Note also that we can control the files that we process. We do so via the filemask in the FOR statement. It we used, for example, "*.EXE", then we would process only executable files.

 GOTO ENDIT
When the mainline of the batch file is finished executing, we goto the end of the batch file. We MUST do this GOTO in order to avoid falling through into, and starting to execute, the first of the batch file's subroutines.

 :PROCESS!
 shift
 shift
Note that when the batch file is called as a subroutine, and the batch file goes to the PROCESS! label, the values of the parms are:

        %0 = [the name of the batch file]
        %1 = SUBROUTINE
        %2 = PROCESS!
        %3 = [name of the file to be processed]
We shift all the parameters to the left twice, to move the parameter(s) into what we think of as the proper places for parameters to the subroutine.

After the first SHIFT command:

       %0 = SUBROUTINE
       %1 = PROCESS!
       %2 = [name of the file to be processed]
After the second SHIFT command:

       %0 = PROCESS!
       %1 = [name of the file to be processed]
Now %1 contains what we think of as the proper parameter(s) to the subroutine. In this case, %1 contains the filename that we want the subroutine to process.

At the end of every subroutine, there should be a GOTO ENDIT, which causes the batch file to go to its own end, and then end and return control to the statement in the program which called it. (This is, of course, the CALL statement embedded in the FOR statement.)

We can optimize the batch file a little by omitting the "goto endit" at the end of the last subroutine. Instead, we simply allow the last subroutine to fall through to the end of the batch file.

This batch file shows how to do work on files that are older than %NumDays%. The PROCESS! subroutine can be modified to do any kind of work you want.

.CODE........:
@echo off
:: A newer version, for use with Windows 9X, Win2000, NT, etc.
::
if (%1)==(SUBROUTINE) goto %2
cls

:: set the number of days in the past.   if this value is not passed
:: in via parameter %1, it defaults to 3 days
set NumDays=%1
if (%NumDays%)==() SET NumDays=3

echo ------------------------------------------------------------------
echo           PROCESSING FILES CREATED MORE THAN %NumDays% DAYS AGO
echo ------------------------------------------------------------------
:: Note the use of ~s in the variable reference.
:: This causes the use of the short (8.3) filename, since Fdate cannot
:: handle long filenames.

for %%I in (*.*) do  CALL %0 SUBROUTINE PROCESS! %%~sI
echo ------------------------------------------------------------------
echo           END OF PROCESSING
echo ------------------------------------------------------------------

:: CLEANUP
set NumDays=
set DaysOld=
set Comparison=
GOTO ENDIT

:PROCESS!
shift
shift

:: get difference in days between filedate and today.
:: Note that /B parm (which is omitted) defaults to today's date.
Fdate /fdif   /A%1        /IF /p"set DaysOld=" >temp.bat
call temp.bat

:: compare DaysOld to NumDays
Fdate /F#comp /A%DaysOld% /B%NumDays%  /p" set comparison=">temp.bat
call temp.bat

:: the following line will DISPLAY THE NAME AND AGE OF
:: any file for which %DaysOld% is greater than %NumDays%
:: --------------------------------------------------------------
if (%comparison%)==(GT) echo %1 is %DaysOld% days old.

:: EXAMPLE (to activate this routine, remove the REM from column 1)
:: the following line will COPY TO AN ARCHIVE SUBDIRECTORY
:: any file for which %DaysOld% is greater than %NumDays%
:: -----------------------------------------------
:: if (%comparison%)==(GT) COPY %1 C:\ARCHIVE\*.*

:: EXAMPLE (to activate this routine, remove the REM from column 1)
:: the following line will DELETE
:: any file for which %DaysOld% is greater than %NumDays%
:: -----------------------------------------------
:: if (%comparison%)==(GT) DEL %1

:: fall through to endit
:endit

.CODE........:
@echo off
:: An older version, for use with DOS
::
if (%1)==(SUBROUTINE) goto %2
cls

:: set the number of days in the past.   if this value is not passed
:: in via parameter %1, it defaults to 3 days
set NumDays=%1
if (%NumDays%)==() SET NumDays=3

echo ------------------------------------------------------------------
echo           PROCESSING FILES CREATED MORE THAN %NumDays% DAYS AGO
echo ------------------------------------------------------------------
for %%v in (*.*) do  CALL %0 SUBROUTINE PROCESS! %%v
echo ------------------------------------------------------------------
echo           END OF PROCESSING
echo ------------------------------------------------------------------

:: CLEANUP
set NumDays=
set DaysOld=
set Comparison=
GOTO ENDIT

:PROCESS!
shift
shift

:: get difference in days between filedate and today.
:: Note that /B parm (which is omitted) defaults to today's date.
Fdate /fdif   /A%1        /IF          /VDaysOld

:: compare DaysOld to NumDays
Fdate /F#comp /A%DaysOld% /B%NumDays%  /Vcomparison

:: the following line will DISPLAY THE NAME AND AGE OF
:: any file for which %DaysOld% is greater than %NumDays%
:: --------------------------------------------------------------
if (%comparison%)==(GT) echo %1 is %DaysOld% days old.

:: EXAMPLE (to activate this routine, remove the REM from column 1)
:: the following line will COPY TO AN ARCHIVE SUBDIRECTORY
:: any file for which %DaysOld% is greater than %NumDays%
:: -----------------------------------------------
:: if (%comparison%)==(GT) COPY %1 C:\ARCHIVE\*.*

:: EXAMPLE (to activate this routine, remove the REM from column 1)
:: the following line will DELETE
:: any file for which %DaysOld% is greater than %NumDays%
:: -----------------------------------------------
:: if (%comparison%)==(GT) DEL %1

:: fall through to endit
:endit