6

I'd like to use FORFILES in a cmd file, to act on all files after a given date thru "today". I can use something like forfiles /d +07/10/2013 /c "cmd /c echo @fname" to act on everything after 7/10/13, but what I want is to just be able to calculate instead from 90 days before "today".

Is there a syntax for the date calculation that will work in a cmd file that will let me specify "x days before today" to feed into FORFILES?

I prefer to not use VBS (and found a code snippet that would work in VBS), though I could alternatively re-write my script for Powershell, but ideally I want to stick with cmd.

To clarify, "-90" would find all files older than 90 days; "+90" would find all files newer than 90 days -after- today (which is fundamentally useless, as files are rarely written with future dates), and "+7/30/2013" will find all files newer than 7/30/2013. I want that last time period, preferably able to take a number-of-days variable passed to the CMD file, that would say "after x number of days before today", i.e. "in the last x days". So instead of using the hard-coded date as shown above, I want to be able to calculate that date within the cmd file.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
Debra
  • 197
  • 1
  • 7
  • Look at my answer [here](http://stackoverflow.com/a/19213777/2098699). – Endoro Oct 10 '13 at 13:16
  • Is a batch file that creates/runs/removes a VBS script, and then runs the forfiles command, any good? – foxidrive Oct 11 '13 at 00:24
  • I want to avoid VBS entirely, for several reasons, though I realize that I'm cutting off one way to do it. – Debra Oct 11 '13 at 01:07
  • Endoro, I think that works but I'm not sure I understand it. Seems that you are building a directory-list file & then removing the entries that are older than the date range. Is that correct? That might actually be fine esp. in this case, I actually am after building a file list that I'm feeding to something else. Please clarify if I'm not parsing correctly, i.e. "how it works". OBTW, I thought I'd searched comprehensively for an answer before posting this question! – Debra Oct 11 '13 at 01:12
  • 2
    Possible duplicate of [FORFILES less than 4 days old](http://stackoverflow.com/questions/19209663/forfiles-less-than-4-days-old) – aschipfl Apr 17 '16 at 21:27

2 Answers2

9

Here is a work-around for the design flaw of forfiles concerning /D +dd days, supposing that no items can be modified in the future, and which is based on two nested forfiles loops and relies on the fact that forfiles sets the ErrorLevel in case no items match the provided search criteria:

rem Define minimum and maximum age in days here (0 means today):
set /A MINAGE=0, MAXAGE=90

set "MAXAGE=%MAXAGE:*-=%" & set "MINAGE=%MINAGE:*-=%" & set /A MAXINC=MAXAGE+1
> nul forfiles /D -%MINAGE% /C "cmd /C if @isdir==FALSE 2> nul forfiles /M @file /D -%MAXINC% || > con echo @fdate  @file"

The outer forfiles loop iterates through items that are at least as old as given by variable MINAGE. The inner forfiles loop, which iterates once at most as it recieves the iterated file @file from the outer loop, returns the same file if it is also at least as old as MAXINC (equals MAXAGE plus 1); if it is not, forfiles sets the ErrorLevel to 1, which in turn is captured by the || operator that executes the following command, namely echo, only in case ErrorLevel has been set to a non-zero value.

Both MINAGE and MAXAGE must not be negative numbers (the commands set "MAXAGE=%MAXAGE:*-=%" and set "MINAGE=%MINAGE:*-=%" remove the minus sign in case). The interim variable MAXINC has been introduced in order to include the age specified by MAXAGE itself into the returned result.

The redirection > nul prevents the outer forfiles from returning empty lines and the inner one from returning items that fulfil its search criteria (because we are interested in those that do not). 2> nul prevents the inner forfiles loop from returning error messages in case its search criteria are violated. > con overrides > nul for the echo command to actually return the required items.

The if @isdir==FALSE part filters out directories so that only files are processed. Change FALSE to TRUE if you want to process only directories; remove it completely if you want to process both.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Great work and love the explanation :) Might be worth stating that negative numbers are prevented with the 2nd/3rd set commands. Also, what are "redired items"? Required or redirected? – Jimadine Nov 14 '17 at 14:54
  • Thank you! I extended the description and fixed the typo as suggested... – aschipfl Nov 15 '17 at 08:51
  • This answer deserves so much more love. Great answer and explanation. – Kelly Bang Jul 30 '18 at 23:28
2

In PowerShell you could do something like this:

$refdate = (Get-Date).AddDays(-90)
Get-ChildItem | Where-Object {
    $_.LastWriteTime -ge $refdate
} | Select-Object -Expand Name
Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • I like the elegant powershell answer, but I've been waffling back & forth between doing this overall process as a cmd file, cmd file that calls a powershell script, or powershell script. After finding FORFILES I thought it was ideal, until I ran into this date issue, because I'm actually building a list of files without extensions, which will feed into some other commands. Thanks for the nifty bit of code, either way. – Debra Oct 11 '13 at 01:14
  • `forfiles` could provide a viable solution if it weren't for the odd behavior you observed (which I consider a design bug). `CMD`/batch in general, however, is terrible at dealing with dates, so I simply wouldn't bother. Just go for PowerShell. – Ansgar Wiechers Oct 11 '13 at 07:11