0

I am trying to organize all files (and directories) on the T: drive into two directories: Pre2010 and PostAnd2010. The files should be organized based on their last modified time--so if file T:\dirA\fileA1 has the timestamp 12/28/2015 03:42pm it should be moved to T:\PostAnd2010\dirA\, maintaining the same file name. Also, if the file T:\dirA\fileA2 has the timestamp 1/13/2009 12:23am it should be moved to T:\Pre2010\dirA\ with the same filename. So the new Pre2010 and PostAnd2010 directories should maintain the same directory structure as the T: drive previously had, but splitting the files by date.

I don't really have any experience writing batch files, but I have been trying! Here's my code so far:

@ECHO OFF
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION

chdir /d T:

FOR /r %%i IN (*) do (

   FOR %%a in (%%i) do set FileDate=%%~ta
   set year=!FileDate:~6,4!

   if !year! LSS 2010 (
      REM Move to T:\Pre2010\<filepath>
      echo !year! is before 2010
   ) else (
      REM Move to T:\PostAnd2010\<filepath>
      echo !year! is 2010 or after
   )

   REM echo !year!
   pause
)

echo Done.
pause

This seemed to be working okay, but then I noticed that the year would occasionally be printed as ~6,4 instead of the actual year. I checked the timestamps of the files that were causing the issue, but nothing seemed strange about them. I am not sure why FileDate and year are blank on some files. I want to figure out this issue before I finish the moving the files portion.

Any help would be greatly appreciated! Thanks!

Logan
  • 1,575
  • 1
  • 17
  • 26
  • Try using the `/a` switch for the `set` for year. – cwahls Dec 29 '15 at 00:15
  • @ClaytonWahlstrom That only seemed to change the "~6,4" to "-7" for the outputs on the messed up ones. – Logan Dec 29 '15 at 00:20
  • Did you do `set /a year=!FileDate:~6,4!`? – cwahls Dec 29 '15 at 00:46
  • 3
    At first, replace the line `FOR %%a in (%%i) do set FileDate=%%~ta` by `set FileDate=%%~ti`; then remove `@echo off` and try to catch an item where `FileDate` is empty... – aschipfl Dec 29 '15 at 01:03
  • @aschipfl Thanks that seemed to work! I got that line from somewhere else and I'm not sure why they used a loop... Could somebody help with the copying portion too? I think `xcopy` is a good choice, but I can't seem to get the variable paths working out. I tried to do `set filepath=!i:~2,999!` then `xcopy "!filepath!" "T:\Pre2010\!filepath!"`, but something is wrong with my filepath variable. I just need to remove the first characters `T:` from the current file's path. – Logan Dec 29 '15 at 17:14
  • The `for %%a` loop was totally senseless, because it enumerated the one item currently iterated by `for %%i` anyway... however, I think the problem you encounter is that you are walking through a directory tree which you are modifying at the same time; according to [this post](http://stackoverflow.com/q/31975093/5047996), `for /R` does not enumerating the entire tree prior to iterating through them; I'll provide an answer for you very soon... – aschipfl Dec 29 '15 at 17:32
  • @aschipfl Thanks. I think I figured it out. I put `set filepath=%%i` outside the IF block and put `set filepath=!filepath:~3!` inside the IF block. However, xcopy is prompting me with `Does specify a file name or directory name on target (F = file, D = directory)?` Do you know if xcopy has a flag that would always resort to directory? I can't seem to find one. – Logan Dec 29 '15 at 17:36
  • @aschipfl Actually, I was able to get around this by doing `echo d | xcopy...` but I am still having issues with xcopy giving me an Access Denied error. – Logan Dec 29 '15 at 18:16
  • See [this answer](http://stackoverflow.com/a/33770152/5047996) concerning the `F`ile/`D`irectory question of `xcopy`... – aschipfl Dec 29 '15 at 18:18
  • @aschipfl Thanks, that portion seems to be working fine though. Would you be able to help at all with the Access Denied error I am receiving? I have tried researching but nothing has worked yet (running as administrator, switching flags around...). – Logan Dec 29 '15 at 18:59

3 Answers3

1

Use ROBOCOPY

robocopy /? for help, particularly:

/MAXAGE:n :: MAXimum file AGE - exclude files older than n days/date.
/MINAGE:n :: MINimum file AGE - exclude files newer than n days/date.
/MAXLAD:n :: MAXimum Last Access Date - exclude files unused since n.
/MINLAD:n :: MINimum Last Access Date - exclude files used since n.
         (If n < 1900 then n = n days, else n = YYYYMMDD date).

Use /L to see what robocopy will do.

Use /MIR to do the actual move.

Copy old files

rem /L to see what it does
robocopy l:\test\fileage\all l:\test\fileage\old /MINAGE:20151002 /L /MIR

or

rem /MIR to actually move the files
robocopy l:\test\fileage\all l:\test\fileage\old /MINAGE:20151002 /MIR

Copy new files

rem /L to see what it does
robocopy l:\test\fileage\all l:\test\fileage\new /MAXAGE:20151002 /L /MIR

or

rem /MIR to actually move the files
robocopy l:\test\fileage\all l:\test\fileage\new /MAXAGE:20151002 /MIR

Creating test files...

What I did was copy c:\windows\system32\nv*.dll l:\test\fileage\all

This gave me a small list of files that had a visible date break before/after the 20151002 date.

You should be able to adapt easily to your directory names.

Kory Gill
  • 6,993
  • 1
  • 25
  • 33
1

Kory Gill's answer leads you to the easiest and most efficient way for your task, using robocopy:

rem Remove the `/L` switch to actually move items:
robocopy "T:\" "T:\Pre2010"     "*.*" /L /E /MOVE /XD "Pre2010" "PostAnd2010" /MINAGE:20100101
robocopy "T:\" "T:\PostAnd2010" "*.*" /L /E /MOVE /XD "Pre2010" "PostAnd2010" /MAXAGE:20100101

However, I want to rely on your code.


You used a for /R loop iterating through all items in the root of T:. The destination directories Pre2010 and PostAnd2010 are also located there. Since you are moving files, you are manipulating the directory tree which is enumerated by for /R. As per this post, for /R does not enumerate the entire directory tree before iterating, so unexpected results may occur. To overcome this, use the dir /B /S command and parse its output by a for /F loop instead, so the entire directory tree is enumerated before the loop actually starts to iterate through all the items. See the following code:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "YEAR=2010"
set "OLDDIR=Pre2010"
set "NEWDIR=PostAnd2010"

chdir /D "T:\"

for /F "eol=| delims=" %%F in ('
    dir /S /B /A:-D "*.*"
') do (
    set "FILEDATE=%%~tF"
    rem The following depends on the region settings:
    if "FILEDATE:~6,4!" LSS %YEAR% (
        xcopy /L "%%~fF" "%%~dF\%OLDDIR%%%~pF"
    ) else (
        xcopy /L "%%~fF" "%%~dF\%NEWDIR%%%~pF"
    )
)
call :CLEANUP

endlocal
exit /B

:CLEANUP
ECHO del /Q "*.*"
for /F "eol=| delims=" %%D in ('
    dir /S /B /A:D "*.*"
') do (
    set "PATHNAME=%%~fD"
    if "!PATHNAME:%OLDDIR%=!"=="!PATHNAME!" (
        if "!PATHNAME:%NEWDIR%=!"=="!PATHNAME!" (
            ECHO rmdir /S /Q "%%~fD"
        )
    )
)
exit /B

Besides the replacement of for /R by for /F and dir /B /S, this is what I did:

  • put the two destination directories and the year into variables OLDDIR, NEWDIR and YEAR, resp.;
  • removed interim variable YEAR and queried FILEDATE:~6,4 immediately;
  • replaced move by xcopy, because move moves files only, so there were only empty directories left finally; so I use xcopy and delete the source files and directories later; remove the /L switch from both xcopy commands to actually copy anything, otherwise it just lists what would be copied;
  • removed some echo and pause statements for debuggung;
  • after the for /F loop, jump to :CLEANUP subroutine;
  • introduced a :CLEANUP routine that deletes everything in the source T:\ except the two destination directories, of course; at first, files in the root dierctory are deleted, then the source directory tree is removed; remove the upper-case ECHOs (twice) to actually delete any source files and directories, otherwise it just outputs what would be deleted;
Community
  • 1
  • 1
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Thank you so much for your help! This is a great answer, but I think I will stick to using robocopy for the sake of simplicity. I do appreciate you working with what I had going though. Thanks again! – Logan Dec 29 '15 at 23:02
0

I appreciate the help from the two answers that I received! They both helped me come to a final solution. I decided to use robocopy instead of the looping I was originally using for simplicity. I ran the script on the drive, which had around 50gb of data and it took a couple hours. I'm slightly concerned by robocopy's output because it reports skipped files and directories; it might be fine though. Here's what I ended up using:

@echo off

chdir /d T:

robocopy T: "T:\Pre2010" /MINAGE:20091231 /S
pause

robocopy T: "T:\PostAnd2010" /MAXAGE:20100101 /S
pause

echo Done!
pause
Logan
  • 1,575
  • 1
  • 17
  • 26