1
forfiles /D +12/1/%2 /C "cmd /c if /I @FDATE LEQ 12/31/%2 move @file %3\Archive\12-%2 >NUL"

The forfiles is supposed to be targeting all files with last modified dates between 12/1/2015 and 12/31/2015 (including the 1st and 31st), and moving them to archive. For reasons unknown, some files with the last modified date 12/3/2015, and 12/9/2015 are not being moved. Additionally, ALL files with last modified date of 12/4/2015 thru 12/8/2015 are not being moved.

Steps taken to debug: Processed different months: January, Feb, etc and Archiving was successful. Permissions have been checked, and verified. Manually archived the unmoved files. Created copies of the unmoved files and still encounter the same issues. The last thing I did was replace

if /I @FDATE LEQ 12/31/%2 move @file %3\Archive\12-%2 >NUL

with

echo @FDATE

to ensure that ALL the modified dates are visible. Modified dates for the files that aren't being successfully moved and archived are visible.

LostInTheSauce
  • 207
  • 1
  • 3
  • 6
  • What is `@FDATE` and `@file`? Do you have an add-on to 'cmd.exe' that you failed to mention? – Bloodied Apr 12 '16 at 16:54
  • `if` command is not capable of comparing dates. It compares either numbers or strings. Try next for proof: `if /I @FDATE LEQ 12/31/%2 (echo @FDATE yes) else (echo @FDATE NO)`. – JosefZ Apr 12 '16 at 17:18
  • 1
    @Arescet, `@file` and `@fdate` are variables used by the `forfiles` command -- type `forfiles `/?` for details; although for most approaches, standard `for` loops can be used, `forfiles` is especially helpful when having to filter files by their age... – aschipfl Apr 12 '16 at 18:06
  • @aschipfl ah, this is what i get for only using `for /r`. – Bloodied Apr 12 '16 at 18:08
  • According to the help shown by `forfiles /?` the `/D` date format should be `dd-MM-yyyy`, at least since Windows Vista and Windows 7... – aschipfl Apr 12 '16 at 18:29
  • @aschipfl /D date Selects files with a last modified date greater than or equal to (+), or less than or equal to (-), the specified date using the "MM/dd/yyyy" format; or selects files with a last modified date greater than or equal to (+) – LostInTheSauce Apr 12 '16 at 18:43
  • @JosefZ if you were to accomplish same task, what method would you choose, if you were limited to .bat? – LostInTheSauce Apr 12 '16 at 19:01
  • Not on my system, although it is an English one, so it seems to depend on locale/region... what Windows version are you using? Windows XP? – aschipfl Apr 12 '16 at 19:27
  • I am using Widows 10 – LostInTheSauce Apr 12 '16 at 19:32
  • I just played with the (short) date format settings of my system and found that `forfiles` seems to accept the date after the `/D` switch in the system's date format... – aschipfl Apr 12 '16 at 21:52

3 Answers3

0

As @JosefZ already pointed out in his comment, if is not capable of comparing date values.

However, to filter files by a certain range for the modification date, you could use two nested forfiles loops:

> nul 2>&1 forfiles /D +01-12-2015 /C "forfiles forfiles /M @file /D -31-12-2105 /C 0x22cmd /C if 00x7840isdir==FALSE > con echo 00x7840fdate  00x7840file0x22"

Development

The outer loop of the two nested forfiles loops enumerates items with the given lower bound of the range for the modification dates (the date is given in format dd-MM-yyyy as per the (short) date format of my system; check out the help text of forfiles /? to find out the proper format for your system):

forfiles /D +01-12-2015

This returns all items modified on 1st of December 2015 or later.

The inner loop is then used to return only items that have been modified on 31st of December 2015 or earlier. To achieve this, we need to create a command line based on the following:

forfiles /M * /D -31-12-2015 /C "cmd /C echo @fdate  @file"

The mask after /M needs to be set to the @file value returned by the outer loop, so the inner one iterates once only per each iteration of the outer one; so the inner loop simply filters each single item received by the outer one by its modification date. The location after /P does not need to be specified as the outer loop executes the inner loop (or in general the command after /C) already from the directory the currently iterated item is located (this is also true if the /S switch is provided to process sub-directories also).

The challenge now is to hide the variables like @file of the inner loop from the outer loop. The trick is the use the hexadecimal code substitution feature of forfiles; for instance, 0x20 will be replaced by a space, 0x22 by a quotation mark, 0x40 by the @-sign and 0x78 is the letter x. Since this substitution is done even before replacement of the @-variables, we need to state 00x7840 to hide the @ symbol from the outer loop and thus to avoid replacement of @file, for example, because the @ will become 0x40, so the inner loop receives 0x40file, which will be converted back to @file and then immediately replaced by its value.

So the command line with the nested forfiles loops could look like this:

forfiles /D +01-12-2015 /C "forfiles forfiles /M @file /D -31-12-2105 /C 0x22cmd /C echo 00x7840fdate  00x7840file0x22"

To filter out directories and return files only, you need to check @isdir:

forfiles /D +01-12-2015 /C "forfiles forfiles /M @file /D -31-12-2105 /C 0x22cmd /C if 00x7840isdir==FALSE echo 00x7840fdate  00x7840file0x22"

Finally, to avoid empty lines or error messages to be returned by forfiles, use redirection:

> nul 2>&1 forfiles /D +01-12-2015 /C "forfiles forfiles /M @file /D -31-12-2105 /C 0x22cmd /C if 00x7840isdir==FALSE > con echo 00x7840fdate  00x7840file0x22"

Consult also the following posts:

Community
  • 1
  • 1
aschipfl
  • 33,626
  • 12
  • 54
  • 99
0

Command line if is not capable of comparing date values. For instance, see false results in date interval between March 30, 2016 and April 10, 2016 (i.e. between 30.3.2016 and 10.4.2016 in terms of my Windows locale):

==> forfiles /D +30.3.2016 /C "cmd /c if @FDATE leq 10.4.2016 (echo @fdate yes @file) else ( echo @fdate NOO @file)"

31.03.2016 NOO "file36322183.txt"
12.04.2016 NOO "inputs.bat"
03.04.2016 yes "mycharmap.bat"
09.04.2016 yes "SO"
12.04.2016 NOO "ttt.txt"

==>

Switching to powershell does not seem to be less cumbersome than nested forfiles:

==> forfiles /D +30.3.2016 /C "powershell -command if ('@FDATE'.ToDateTime([Globalization.CultureInfo]::CurrentCulture) -le '10.4.2016'.ToDateTime([Globalization.CultureInfo]::CurrentCulture)) { echo '@fdate yes @file'} else { echo '@fdate NOO @file'}"

31.03.2016 yes file36322183.txt
12.04.2016 NOO inputs.bat
03.04.2016 yes mycharmap.bat
09.04.2016 yes SO
12.04.2016 NOO ttt.txt

==>

Powershell command gives right results. However, I'd test @isdir property as well (see SO folder in both above lists).

JosefZ
  • 28,460
  • 5
  • 44
  • 83
0

Here's what I FINALLY got to work:

SET Backups=C:\Users\%username%\Documents\Backups

forfiles /P %Backups%\Backups--Cisco_Smartnet /m  *.xls /D -90 /C 

"cmd /c move @file %Backups%\Backups--Cisco_Smartnet\Old_Backups--

3+_Months_Old

For me, this is to move spreadsheets (.xls) that are 90 day old or older (/D -90) to the old backups folder. @file is any file in the directory that meets this criteria. The %backups% variable is not important for this command, it just saves me time when I'm doing a lot of these. The SET line is its own line. And from Forfiles to the end is its own line.

I hope this helps, have fun!