Batch file to move files by date modified - windows

I have written a batch file, which creates empty folders for each date. My next task is to create another batch file, which moves each file in a directory, into the relevant date folder, based on their date modified. I have read numerous forums and articles on how I can achieve this, but with my limited batch file knowledge, I just cannot seem to get it to work. The code I currently have is shown below, though this does not seem to pull in the date modified. Any help is much appreciated!
SET directory="\directory\path\archive"
FOR /f %%a in ('dir /b "%directory%"') do (
SET fdate=%%~Ta
MOVE "%directory%\%%a" "%directory%\%fdate%"

Until you provide more info as to format of dates, I can't give a definitive answer. But I can show you how I would do it on my machine.
I use yyyy-mm-dd format within file and folder names, so December 13, 2011 would be 2011-12-13. My machine uses mm/dd/yyyy format for dates (12/13/2011). So I would need to translate the %%~tF output from 12/13/2011 into 2011-12-13. Note - / cannot be used used in file or folder names.
So this code would do what you want on my machine:
set "source=\directory\path\archive"
set "targetRoot=\directory\path\archive"
for %%F in ("%source%\*") do (
for /f "tokens=1,2,3 delims=/ " %%A in ("%%~tF") do (
move "%%~fF" "%targetRoot%\%%C-%%A-%%B"
)
)
Addendum - Question in comment asks for method to left pad a number with zeros for dir creation. I see two easy choices. (This really should be a different question)
This first method is simple but tedious and not practical as a general solution
for %%A in (01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31) do ...
The second method is a general solution. Since your assignment is within parentheses, you need to use delayed expansion.
setlocal enableDelayedExpansion
for /l %%A in (1 1 31) do (
set "day=0%%A"
set "day=!day:~-2!
...
)
You increase the number of leading zeros by adding more 0 to the front and then increasing the number of characters you preserve in the substring operation.
BUT - why prepopulate the directories? Your strategy will add directory days that don't exist in the calendar, plus you are likely to have many unused folders for which no files were modified on that day. Better to create the folders only as needed. Then the 0 padding is already done for you, and no unneeded folders are created.
set "source=\directory\path\archive"
set "targetRoot=\directory\path\archive"
for %%F in ("%source%\*") do (
for /f "tokens=1,2,3 delims=/ " %%A in ("%%~tF") do (
if not exist "%targetRoot%\%%C\%%A\%%B" mkdir "%targetRoot%\%%C\%%A\%%B"
move "%%~fF" "%targetRoot%\%%C\%%A\%%B"
)
)

My answer is a simple FOR loop who check all files in current directory and take date from eachone file. Create a folder in format yyyy\yyyy-mm and move file into it.
#echo off
rem not %var% but !var! give us access to internal loop variable value with enabledelayedexpansion.
setlocal enabledelayedexpansion
FOR %%V IN (*.*) DO (
SET filedate=%%~tV
rem echo !filedate!
SET fileyear=!filedate:~6,4!
rem echo !fileyear!
SET filemonth=!filedate:~3,2!
rem echo !filemonth!
rem create directory as yyyy\yyyy-MM
mkdir !fileyear!\!fileyear!-!filemonth! 2>nul
echo move %%V !fileyear!\!fileyear!-!filemonth!\
move %%V !fileyear!\!fileyear!-!filemonth!\ >nul
)
TIP: The filemonth and fileyear variable probably should have other numbers to cut proper yyyy/mm for your country/region timestamp.
TIP: This FOR move the script itself to into one of subdirectory, I run it as Windows Shortcut with enter a Location Path - then this script run in proper place.

Related

batch - execute command for every file with specific creation date

got this piece of code:
forfiles /P %ParentFolder% /S /M %Format% /C "cmd /c %exeFile% #path"
executing some exe for every file matching format as parameter.
any way to add "creation date" as a condition to run command via CMD?
something like :
for all files in directory (recursive) X if creation date newer then 1 day ago do (run) some exe with this file's path as param
This is not possible with forfiles, because, when the /D option is provided, it only regards the last modification date only (not even the modification time).
Unfortunately, there are no native commands for date/time maths, so I suggest to switch to a language that is capable of that; for instance, PowerShell, VBScript, JavaScript (which are all native to Windows past XP).
In case the modification date could be used, and a simple check with the date of today is sufficient, the following forfiles command line could be used:
forfiles /S /P "%ParentFolder%" /M "%Format%" /D +0 /C "cmd /C \"%exeFile%\" #path"
The /D option with a non-negative number lets forfiles return files that have been modified the given number of days after today or later (although you would need a time-machine; hence I consider this a design flaw). For +0 as the given number of days, all matching files modified today are returned, because forfiles /D only checks the modification date but does not care about the modification time.
If a simple equality check of the creation date with the date of today is fine for you, it can be done in batch-file scripting quite easily though (see all the explanatory rem remarks for how the following script works):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "ParentFolder=."
set "Format=*.*"
set "exeFile=" & rem // (full path to executable file)
set "tmpFile=%TEMP%\%~n0_%RANDOM%.tmp"
rem // Create a temporary file and retrieve its creation date:
2> nul del "%tmpFile%" & > "%tmpFile%" break & set "TODAY="
for /F "skip=5" %%J in ('dir /N /4 /-C /T:C "%tmpFile%"') do (
if not defined TODAY set "TODAY=%%J" & del "%tmpFile%"
)
rem // Change to predefined parent directory:
pushd "%ParentFolder%" && (
rem // Return all files recursively:
for /F "delims=" %%I in ('dir /B /S /A:-D "%Format%"') do (
rem // Determine the creation date for the current file:
set "FIRST=#"
for /F "skip=5" %%J in ('dir /N /-C /T:C "%%I"') do (
rem // Regard line listing file only, ignore summary lines:
if defined FIRST (
set "FIRST="
rem // Check creation date against today:
if "%%J"=="%TODAY%" (
rem // Return files created today:
echo "%%I" has been created today.
rem // Run external program on found file:
if defined exeFile "%exeFile%" "%%I"
)
)
)
)
rem // Restore previous working directory:
popd
)
endlocal
exit /B
I am using two dir command lines here:
the first one returns a bare list of files recursively (/S; no directories because of /A:-D) without any dates/times, headers and footers, due to switch /B; not using this switch would lead to header and footer lines for the whole output and for every iterated sub-directory also, so the output would be quite complicated to be parsed;
the second one receives each file returned by the first one; since there is no /B but the /N option, the file creation date/time is returned (/T:C); for every file the output looks like this:
Volume in drive D is DATA
Volume Serial Number is 0000-0000
Directory of D:\Data
2016/09/29 16:00 1024 current_file.txt
1 File(s) 1024 bytes
0 Dir(s) 1099511623680 bytes free
the first token of the sixth line constituting the creation date is split off and compared against the current date %DATE%; to ignore the header, the skip=5 option of the for /F loop is used; to ignore the summary lines, the variable FIRST is used;
Note that the date format is locale-dependent; as long as there appear no spaces in the date, this is no problem as the current date is also determined by the dir command applied on a temporary file.
Looking at the forfiles /? shows the switch /D that will exclude files that are younger (/d -<days>) or older (/d +<days>) than the given value for <days>.
As you want the files from today, you would set <date> to 0.
Notice, that this will look on the changed date!
Other way would be to use a for /r-loop and get a list of the creation date with dir /T:C ; to sort it add /O-D. Then separate that using a for /f loop to get the lines of the output of dir and another one to separate it (possibly easier without the nested loops).
You can than compare the creation date with %date% or when using one day=24 hrs compare the creation time with %time% additionally.

Windows batch file renaming via reordering/manipulating current name

I have a large amount of files in multiple folders, the file name format is:
FILE DESCRIPTION_MM DD YYYY_F_XXXXX.EXTENSION
The FILE DESCRIPTION is a variable amount of characters. XXXXX is a variable amount of characters as well. The extension is either pdf, doc or docx. The delimiter to be used is the first instance of _. Example:
Foo File_01 01 2016_F_Bar.pdf
I need to rename each file by reordering the Date to the front. Instead of the current format, it needs to be
MM DD YYYY_FILE DESCRIPTION_F_XXXXX.EXTENSION".
Thus I need to cut out DATE and the 2nd instance of _ and move it to the front of the filename. This amounts to 11 characters in total including the _.
The extension needs to be unchanged. Using the above example:
01 01 2016_Foo File_01 01 2016_F_Bar.pdf
Also I only want to rename the files in the format "FILE DESCRIPTION_MM DD YYYY_F_XXXXX.EXTENSION" ignoring all other cases in which files are already formatted with the DATE in the front and cases where there is no DATE.
Any help would be greatly appreciated!
Give this a try on some test data
#echo off
FOR /F "delims=" %%G IN ('dir /a-d /b /s *_*_*.pdf *_*_*.doc *_*_*.docx ^|findstr /R /C:".*_[0-9][0-9] [0-9][0-9] [0-9][0-9][0-9][0-9]_F_.*\."') DO (
FOR /F "TOKENS=1,2* delims=_" %%H IN ("%%~nxG") DO rename "%%~G" "%%~I_%%~H_%%~J"
)

How to permanently delete specific file after specific number of days after running the script in a batch file

I would like to have a little batch file script which will work on Windows XP,7,8 and delete any occurrences of a specific file after a certain amount of time after the script is executed.
I have tried something similar:
forfiles -p "C:\what\ever" -s -m *somefile.pdf* /D -<number of days> /C "cmd /c del #path"
But this isn't what I'm actually looking for as it is deleting files older than a specific time. Any useful advice ?
There are basically three ways to solve this problem:
Keep the Batch file running all the time, delete the file after the desired time complete and restart the process. This is the method used in wOxxOm's answer.
Use Windows Task Scheduler to execute the "delete file" command every N days.
Place a Batch file in Startup folder so it run each time the user log in. The Batch file check if the file must be deleted to do it, calculate the date of the next time and store it in a companion data file. This is the method used in the Batch file below:
.
#echo off
setlocal EnableDelayedExpansion
rem Get today's date arranged as YYYYMMDD
rem (adjust lines below if MM/DD/YYYY is not the current locale date format)
for /F "tokens=1-3 delims=/" %%a in ("%date%") do (
set "MM=%%a" & set "DD=%%b" & set "YYYY=%%c"
)
set "today=%YYYY%%MM%%DD%"
rem Read next date from companion file
set /P nextDate=< NextDate.txt
if %today% lss %nextDate% goto :EOF
rem Delete the file, calculate next date and store it in companion file
del "C:\what\ever\somefile.pdf"
set days=5
set mon=0
for %%a in (31 28 31 30 31 30 31 31 30 31 30 31) do (
set /A mon+=1
set "daysPerMonth[!mon!]=%%a"
)
set /A Ymod4=YYYY %% 4, MM=1%MM% - 100, DD=1%DD% - 100 + days
if %Ymod4% equ 0 set "daysPerMonth[2]=29"
if %DD% gtr !daysPerMonth[%MM%]! set /A DD-=daysPerMonth[%MM%], MM+=1
if %MM% gtr 12 set /A MM=1, YYYY+=1
if %MM% lss 10 set "MM=0%MM%"
if %DD% lss 10 set "DD=0%DD%"
echo %YYYY%%MM%%DD%> NextDate.txt
Note that you must manually create the NextDate.txt file with the first date, although additional code may be inserted in order to manage this initialization step.
Using only the built-in commands:
set days=5
for /L %%T in (1,1,%days%) do timeout 86400
del "C:\what\ever\*somefile.pdf*"
This will pause the execution of the batch file for the given number of days, then it'll delete the file(s).

append file date to file name using cmd

Hi I'd like to create a bat file to rename files using the cmd. Say a friend and I went on vacation together. We have put all our pictures together and I now want to rename all the pictures in a sequence.
Say I selected the following pictures:
mypicture_3435.jpg (file date = 01 Jan 2015 10:00)
mypicture_3465.jpg (file date = 04 Jan 2015 12:00)
myfriendspicture_2221.jpg (file date = 03 Jan 2015 12:00)
myfriendspicture_2226.jpg (file date = 04 Jan 2015 11:00)
I would like to get the following output:
ourvacation_01.jpg [mypicture_3435.jpg (file date = 01 Jan 2015
10:00)]
ourvacation_02.jpg [myfriendspicture_2221.jpg (file date = 03
Jan 2015 12:00)]
ourvacation_03.jpg [myfriendspicture_2226.jpg (file
date = 04 Jan 2015 11:00)]
ourvacation_04.jpg [mypicture_3465.jpg
(file date = 04 Jan 2015 12:00)]
This is easy to do with specific software such as acdsee or even with Windows' image browser. But I would like to do it using the command promt. (I teach mathematics in a school and I would like to use this as a programming example).
I tried the follwing script and it worked:
#echo off
setlocal EnableDelayedExpansion
set i=0
for %%a in (*.jpg) do (
set /a i+=1
ren "%%a" "!i!.new"
)
ren *.new *.jpg
But it renamed the files like this:
myfriendspicture_2221.jpg becomes 1.jpg
myfriendspicture_2226.jpg becomes 2.jpg
mypicture_3435.jpg becomes 3.jpg
mypicture_3465.jpg becomes 4.jpg
So the problems are:
They do not keep a chronological sequence.
If I have say 11 items, and sort them by name, the sequence runs 1, 10, 11, 2, 3, 4...
I tried to run dir /od before I rename, but it didn't work, the sequence runs according to the file name.
All the strings I have found regarding this issue append the current date and time to the file name, but not the file date and time to it's own name.
I couldn't find a way to add a leading 0 or 00 to the sequence so that the file name and chronological order match.
Thank you very much in advance for the help.
I tried to run dir /od before I rename, but it didn't work, the sequence runs according to the file name.
Did you simply run dir /od and then for %%a in (*.jpg)? How did you plan to capture the output of dir /od to make it useful, rather than just dumping it to stdout?
Whenever you want to capture the output of a command, use a for /f loop. You should combine the two commands like this:
for /f "delims=" %%I in ('dir /b /o:d *.jpg') do stuff.
Next issue: zero left padding the file names. Can we assume you won't have more than 10,000 pics from your vacation? Then just prepend four zeros to your generated filename, then use the right-most 4 characters to determine the base name of your pics.
#echo off
setlocal enabledelayedexpansion
set /a seq=10001
for /f "delims=" %%I in ('dir /b /o:d *.jpg') do (
rem :: remove the "echo" when you are ready to rename
echo ren "%%~I" "ourvacation_!seq:~-4!.jpg"
set /a seq += 1
)
The result will look a little something like this:
C:\Users\me\Desktop>test.bat
ren "avatar65929_2.jpg" "ourvacation_0001.jpg"
ren "IMG_20140621_190332.jpg" "ourvacation_0002.jpg"
ren "funny-Finding-Neverland-scene-Explorer.jpg" "ourvacation_0003.jpg"
ren "5DFPwMa.jpg" "ourvacation_0004.jpg"
ren "funny-root-math-equation.jpg" "ourvacation_0005.jpg"
ren "196889_145889312230730_1041953273_n.jpg" "ourvacation_0006.jpg"
ren "0001-cf72d77a-509ac348-b92c-fe8af7d2.jpg" "ourvacation_0007.jpg"
ren "IMG_20141230_191526.jpg" "ourvacation_0008.jpg"
ren "20150120_142150.jpg" "ourvacation_0009.jpg"
ren "20150120_145223.jpg" "ourvacation_0010.jpg"

Batch file setting variable in if/else statement in For Loop

I'm trying to create a batch file that generates a custom AVIsynth script per each file. Right now the batch file is set to execute from within the folder where the video files exist. What I need to do is get the creation time of the file to generate a timecode burn in. I have no problem getting the info I need. However, if the file was created in the afternoon I need it to be in 24hr time. For example, 2pm needs to display as 14.
I have a working if statement that creates a newth variable that adds 12 if need be. However, if it doesn't need it the variable persists. On each subsequent iteration of the loop the variable doesn't change.
My example. I have two files the first was created at 2pm the other at 12pm. The 2pm file is read first and the newth variable becomes 14. So far so good. On the next file the newth variable should become 12 but instead remains 14. How do I fix this?
#Echo Off & CLS
SetLocal EnableDelayedExpansion
For /F %%a In ('dir *.mpg /b') Do (
ECHO Processing "%%a"
echo %%~ta
set time=%%~ta
set th=!time:~11,2!
set tm=!time:~14,2!
set era=!time:~17,2!
echo !era!
if "!era!"=="PM" (
if !th! LSS 12 ( set /a newth=!th!+12 )
) else ( set /a newth=!th!)
echo !newth!
echo //AviSynth Test Script >scripts/%%a.avs
echo DirectshowSource^("%%~fa"^)>>scripts/%%a.avs
echo LanczosResize^(720,400^) >>scripts/%%a.avs
echo ShowSMPTE^(^) >>scripts/%%a.avs
ECHO Back to Console
Pause
)
It's a little messy because I've been using echo for debugging. But hopefully the problem is clear.
Here is a method with Wmic - Wmic is in XP pro and above.
#Echo Off & CLS
SetLocal EnableDelayedExpansion
For /F "delims=" %%a In ('dir *.mpg /b') Do (
ECHO Processing "%%a"
set "file=%cd%\%%a"
set "file=!file:\=\\!"
WMIC DATAFILE WHERE name="!file!" get creationdate|find ".">file.tmp
for /f %%a in (file.tmp) do set dt=%%a
set tm=!dt:~8,2!:!dt:~10,2!:!dt:~12,2!
del file.tmp
echo !tm!
echo //AviSynth Test Script >scripts/%%a.avs
echo DirectshowSource^("%%~fa"^)>>scripts/%%a.avs
echo LanczosResize^(720,400^) >>scripts/%%a.avs
echo ShowSMPTE^(^) >>scripts/%%a.avs
ECHO Back to Console
Pause
)
There are a few problems with your code. The major one is this sequence
if "!era!"=="PM" (
if !th! LSS 12 ( set /a newth=!th!+12 )
) else ( set /a newth=!th!)
With your first filetime "02:xx PM"
th=02, era=PM, so set /a newth=02+12 sets newth=14
With your second filetime "12:xx PM"
th=12, era=PM, so - do nothing, since there's no else action for !th! LSS 12
Hence, newth remains at 14.
So - what's the fix? Since you don't use newth further, we can't say for certain, but it appears you want 24-hour format - 4 digit hhmm.
DANGER, Will Robinson moment number 1:
You are dealing with numbers starring LEADING ZEROES. All well and good except where the value is 08 or 09, which batch bizarrely interprets as OCTAL since it begins 0.
DANGER, Will Robinson moment number 2:
set /a will suppress leading zeroes, so set /a newth=!th! will set newth to 7 for time 07:36 AM - not 07...
So - how to overcome all this?
IF !th!==12 SET th=00
SET th=!th: =0!
if "!era!"=="PM" (set /a newth=1!th!+12
SET newth=!newth:~-2!
) else ( set newth=!th!)
This forces 12 AM to 00 AM and 12 PM to 00 PM
Then replace any spaces with 0 (in case you have leading spaces, not zeroes)
Then, if era is PM, add 100 by stringing 1 before the 2-digit hour number, add 12 and grab the last 2 characters
Otherwise, just use the number in th
Unfortunately, made a little more complicated since you haven't told us whether you use or don't use leading zeroes in your time format. Nevertheless, the incomplete original calculation method is at fault.
DANGER, Will Robinson moment number 3:
time is a MAGIC VARIABLE - and you know what happened to Mickey when he got involved in things better left alone.
If you set time in a batch, then %time% or !time! will return the value you set. If you don't set it, then the value returned will be the system time. Same goes for DATE and a number of similar values (see set /? from the prompt - there's a list at the end)
here's how you can get the time stamp with seconds:
C:\>powershell -command "& {(gwmi -query """select * from cim_datafile where name = 'C:\\file.txt' """).lastModified;}"
C:\>powershell -command "& {(gwmi -query """select * from cim_datafile where name = 'D:\\other.txt' """).creationdate;}"
I've tried with WMIC but still cannot get the time stamp.As you are using Win7 you should have powershell installed by default.

Resources