0

I have a directory that is quite large and would like to move the folders/files that are older than let's say 90 days to another directory.

My starting point is this cmd command

forfiles /s /m *.* /d -90 /c "cmd /c echo @file is at least 90 days old."

This returns the files that are older than 90 days, but I need to replace the echo with a move command. I am not sure how this will work with regard to bringing the files over intact with their parent directories.

Would this work to keep the folder structure?

forfiles /s /m *.* /d -90 /c "move @file c:\temp"

Would anyone have any advice or foresee any issues in trying to do it this way?

Robocopy is not an option.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
Krom
  • 37
  • 1
  • 7
  • The Stack Overflow search [\[batch-file\] or \[cmd\] move files older than](http://stackoverflow.com/search?q=%5Bbatch-file%5D+or+%5Bcmd%5D+move+files+older+than) returns 75 hits. So this was definitely not asked the first time. I suggest that you read the found questions and answers. The main problem on using Windows command interpreter is no built-in support for time difference calculations. Better would be using PowerShell. The main batch file/cmd topic for all "older than" questions is: [Batch file to delete files older than N days](http://stackoverflow.com/questions/51054/). – Mofi Feb 21 '17 at 17:58

1 Answers1

4

forfiles executes the command line after the /C option with the currently iterated directory as the working directory. This is also true with the /S option.

The reference @file returns the pure (quoted) file name; the reference @relpath returns a path relative to the given root (behind the /P option, which defaults to the current directory).

So you could try something like this (note that the cmd /C prefix is required for cmd-internal commands like move, echo or if; the upper-case ECHO just displays the move command line that would be executed without):

forfiles /S /D -90 /C "cmd /C if @isdir==FALSE ECHO move @relpath 0x22C:\temp\0x22"

This would move all files into the directory C:\temp, losing the original directory hierarchy however. (Note that the if @isdir==FALSE query prevents sub-directories from being processed.)

Therefore we need to build the destination directories on our own, like this:

forfiles /S /D -90 /C "cmd /C if @isdir==FALSE (for %F in (@relpath) do @(2> nul mkdir 0x22C:\temp\%~F\..0x22 & ECHO move @relpath 0x22C:\temp\%~F0x22))"

What happens here:

  • in general, 0x22 represents a quotation mark ";
  • if @isdir==FALSE ensures to process files only;
  • the for loop just removes surrounding quotes from the path retrieved by @relpath when accessed by %~F; ensure to double the % signs in case you are using the code in a batch file!
  • mkdir creates the parent directory of the currently iterated item; 2> nul hides the error message in case the directory already exists;
  • move is preceded by ECHO for testing purposes; remove it to actually move files;

If you want to overwrite files in the destination location, simply add the /Y option to move.


The following command line might also work for the sample path, but it is going to fail for sure in case the destination path contains SPACEs or other poisonous characters, because quotation is not properly handled:

forfiles /S /C "cmd /C if @isdir==FALSE (2> nul mkdir C:\temp\@relpath\.. & ECHO move @relpath C:\temp\@relpath)"
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Beautiful answer, With regard to this command(apologies I don't know how to format it like you did above) forfiles /S /D -90 /C "cmd /C if @isdir==FALSE (for %F in (@relpath) do @(2> nul mkdir 0x22C:\temp\%~F\..0x22 & ECHO move @relpath 0x22C:\temp\%~F0x22))" Is this going to work if there are sub directories of sub directoes with files in them? In my case its possible I could move the top level folder based on its own created date, would this be easier? – Krom Feb 22 '17 at 15:33
  • Yes; there is the `/S` switch which tells `forfiles` to enumerate also sub-directories; even if the sub-directories themselves may not match the filters (`/D`, `/M`), any contained sub-items are still returned if they match. Note that `forfiles /D` does not care about the creation dates, it only regards the last modification dates of files and directories... – aschipfl Feb 22 '17 at 17:45
  • Googling around I see no switch that operates on the creation date of the files/folders at all for that matter. I believe this command will delete a folder that's older than 90 but again because the /D it is based off of the modified date. forfiles /p "c:/temp" /s /m *.* /c "cmd /c Del @path" /d -90 The problem is someone could modify the file at 89 days and the script would then move everything but that file likely creating a large mess. Perhaps this is not the best way to try to do this as going base off the top level directory created date would be the best way and im not sure I can. – Krom Feb 23 '17 at 14:06
  • The command line you mention would enumerate files and folders 90 days old or more, but it would delete files only (no folders) due to the `del` command (this cannot delete folders). Anyway, `forfiles` does not recognise creation dates nor last access dates. If you need that, you need to switch to something else (perhaps `dir /T:C`, captured by `for /F`, but this returns locale-dependent dates/times; or you go for PowerShell, which is capable of handling relative dates/ages properly)... – aschipfl Feb 23 '17 at 14:31
  • Yeah i am surprised that this is so hard to find for me. I feel that this should be a one liner in some language. Iterate through folders for each folder if the created date is more than 90 days ago move it to another folder. – Krom Feb 23 '17 at 18:24
  • 1
    @Krom You hopefully know that the creation date of a file/directory is always set to __current date__ when a directory or file is the first time created in __that directory__. So if you copy a file last modified 2 years ago from one directory to another directory, the destination file has same last modified date as the source file in source directory, but its creation date is the current date. This means the creation date of the file in destination directory is 2 years after last modification date of the file. For that reason the creation date is nearly never used as not really useful. – Mofi Feb 25 '17 at 21:12
  • 1
    @Krom In other words the creation date of a file gives information on how long the file already exists in __that directory__ without being temporarily deleted and not how old the file is respectively when the contents of the file was first time saved. – Mofi Feb 25 '17 at 21:12