I would like to keep the X latest files from a folder and delete the rest. Is this possible with FORFILES? If it's not I can fallback to another solution I seen here. Thanks for help.
I did this but it takes by dates: EDIT
forfiles /p [path] /s /d -5 /c "cmd /c echo #file"
(echo file for testing purpose)
#ECHO OFF
SETLOCAL
SET "targetdir=U:\destdir"
SET /a retain=10
FOR /f "skip=%retain%delims=" %%a IN (
'dir /b /a-d /o-d "%targetdir%\*" '
) DO ECHO (DEL "%targetdir%\%%a"
GOTO :EOF
You would need to change the setting of targetdir to suit your circumstances. Equally, this procedure targets all files - change the filemask to suit.
The required DEL commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(DEL to DEL to actually delete the files.
method is to simply execute a dir in basic form without directories, sorted in reverse-date order.
Skip the first 10 entries, and delete the rest.
With forfiles I see no chance to accomplish your task of returning the newest (most recent) number of files.
So my idea for this approach is this:
to use dir /B /A:-D /T:C /O:-D to retrieve a bare list (/B) of files (no directories, /A:-D), sorted by creation date (/T:C; if you want to use the last modification date, simply remove the /T:C portion) in decending order (/O:-D), meaning newest items first;
to put over a for /F "eol=| delims=" loop to gather and parse the dir output line by line, meaning file by file, not excluding file names beginning with ; (eol=|, | is illegal for file names) and not splitting file names containing white-spaces like SPACE or TAB (delims=);
to establish a variable that constitutes a counter, incremented per each loop iteration;
to place an if condition inside of the loop to check if the counter reached the desired limit number and in case it is fulfilled, to break the for /F loop by goto;
Here is the related code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Define global constants here:
set "TARGETPATH=\path\to\files\*.*"
set /A "TOTAL=10"
set /A "COUNT=0"
for /F "eol=| delims=" %%F in ('
dir /B /A:-D /T:C /O:-D "%TARGETPATH%"
') do (
echo(%%F
set /A COUNT+=1
setlocal EnableDelayedExpansion
if !COUNT! GEQ %TOTAL% (
endlocal
goto :NEXT
) else (
endlocal
)
)
:NEXT
endlocal
exit /B
I toggled the delayed variable expansion within the for /F loop to avoid trouble in case file names contain exclamation marks !, which would get lost in the line echo(%%F in case it is on.
Update
The following code accomplishes the original task of your question, namely to delete files in a given directory but to keep the most recent number of files:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Define global constants here:
set "TARGETPATH=\path\to\files\*.*"
set /A "TOTAL=10"
set "SKIPOPT=" & if %TOTAL% GTR 0 set "SKIPOPT=skip=%TOTAL% "
for /F "%SKIPOPT%eol=| delims=" %%F in ('
dir /B /A:-D /T:C /O:-D "%TARGETPATH%"
') do (
del /P "%%F"
)
endlocal
exit /B
Since for /F supports a skip= to skip the given number of lines, and so files in our situation, let us make use of it. It is given indirectly here via variable SKIPOPT, which holds the entire option string like skip=10 (given that TOTAL is set to 10). The if %TOTAL% GTR 0 query is implemented for the script not to fail in case TOTAL is 0, because for /F does not accept the option skip=0.
The /P switch at the del command lets appear a prompt Delete (Y/N)? for testing purposes. If you do not want any prompts, simply remove it.
Related
i have loads of files which i want to organize differently. The batch script should create folders with the substring on the left side of the date in the filename.
Files are now named like this:
This_is_my_file_21.01.29_22-00_abc_115.avi
This_is_my_file_20.09.29_21-10_abc_15.avi
This_is_another_file_21.01.29_22-00_abc_55.avi
Pattern:
<Name with unknown number of underscores>_<YY.MM.DD>_<hh-mm>_<string with unknown length>_<number n from 1-999>.avi
Folders should be named like this:
This_is_my_file <- two files will go into this directory
This_is_another_file <- only one file.
The Problem is, how do I get the correct substring for my folder name?
This is what I have so far:
#echo off
setlocal
set "basename=."
for /F "tokens=1* delims=." %%a in ('dir *.avi /B /A-D ^| sort /R') do (
set "filename=%%a"
setlocal EnableDelayedExpansion
for /F "delims=" %%c in ("!basename!") do if "!filename:%%c=!" equ "!filename!" (
set "basename=!filename!"
md "!basename:~0,-23!"
)
move "!filename!.%%b" "!basename:~0,-23!"
for /F "delims=" %%c in ("!basename!") do (
endlocal
set "basename=%%c
)
)
#ECHO OFF
SETLOCAL
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
FOR /f "delims=" %%b IN ('dir /b /a-d "%sourcedir%\*.avi" ' ) DO (
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :countus "%%b"
IF DEFINED subdir (
MD "!subdir!" 2>NUL
ECHO MOVE "%sourcedir%\%%b" "%sourcedir%\!subdir!\"
) ELSE (
ECHO Failed pattern check %%b
)
ENDLOCAL
)
GOTO :EOF
:: count number of underscores before pattern YY.MM.DD_hh-mm
:countus
SET /a ucount=0
:countusloop
SET /a ucount+=1
SET /a scount=ucount+1
FOR /f "tokens=%ucount%,%scount%delims=_" %%q IN ("%~1") DO SET "str1=%%q"&SET "str2=%%r"
IF NOT DEFINED str2 SET "subdir="&GOTO :EOF
:: is %str1%.%str2:-=.%. of form np.np.np.np.np where np is a number-pair?
SET "candidate=%str1%.%str2:-=.%."
FOR /L %%c IN (10,1,99) DO IF DEFINED candidate SET "candidate=!candidate:%%c.=!"&IF NOT DEFINED candidate GOTO success
FOR /L %%c IN (0,1,9) DO IF DEFINED candidate SET "candidate=!candidate:0%%c.=!"&IF NOT DEFINED candidate GOTO success
GOTO countusloop
:success
SET "subdir=%~1"
FOR /f "delims=:" %%e IN ("!subdir:_%str1%_%str2%=:!") DO SET "subdir=%%e"
GOTO :eof
The "move" command is merely echoed for verification. Remove the echo from echo move to actually move the files.
This possible solution uses the fact that your filenames have a known number of underscores if you work backwards. All I do is replace those underscores with backslashes, which obviously cannot already be contained in the filename. I can then use the relative paths to step up the filename, as if it were a directory tree, until all I have left is the part ahead of the date sequence, which I then replace the backslashes with underscores again. I use the result of that with robocopy, which has a move option, and will create the destination directory automatically, if it does not already exist. At the outset, I perform the directory search, in the same directory as the batch-file, using where.exe, (you can change that, on line three, from "%~dp0." to ".", if you want to use the current directory instead, or "any other path" as necessary). where.exe not only treats the ? wildcard as exactly one character, (unlike the dir command which is zero or one), but also ignores 8.3 naming. It therefore treats the .avi extension exactly as written, (and not 'beginning with' .avi, which dir, or a standard for loop, would).
Anyhow, feel free to give it a try:
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "}=" & For /F Delims^= %%G In ('(Set PATHEXT^=^) ^& %__AppDir__%where.exe
"%~dp0.":"?*_??.??.??_??-??_?*.avi" 2^> NUL') Do (Set "}=%%~nG"
SetLocal EnableDelayedExpansion & For %%H In ("\!}:_=\!") Do (
EndLocal & For %%I In ("%%~pH..\..") Do (Set "}=%%~pI"
SetLocal EnableDelayedExpansion & Set "}=!}:~1,-1!"
For %%J In ("!}:\=_!") Do (EndLocal & %__AppDir__%robocopy.exe ^
"%%~dpG." "%%~dpG%%~J" "%%~nxG" /Mov 1> NUL))))
If you want even further robustness, and do not wish to use a more suitable scripting technology, the following, extremely complex looking, version, is the same code, except that it uses findstr to validate the date and time sequence. It filters those avi files containing the following pattern, _yy.MM.dd_hh-mm_ in the avi filenames, using all dates from the beginning of 1970 up until the end of 2021:
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "}=" & For /F Delims^= %%G In ('(Set PATHEXT^=^) ^& %__AppDir__%where.exe
"%~dp0.":"?*_??.??.??_??-??_?*.avi" 2^> NUL ^| %__AppDir__%findstr.exe
/RC:"_[789][0123456789].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].3[01]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].3[01]_2[0123]-[012345][0123456789]_"
/C:"_2[01].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
/C:"_2[01].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].1[012].3[01]_2[0123]-[012345][0123456789]_"') Do (Set "}=%%~nG"
SetLocal EnableDelayedExpansion & For %%H In ("\!}:_=\!") Do (
EndLocal & For %%I In ("%%~pH..\..") Do (Set "}=%%~pI"
SetLocal EnableDelayedExpansion & Set "}=!}:~1,-1!"
For %%J In ("!}:\=_!") Do (EndLocal & %__AppDir__%robocopy.exe ^
"%%~dpG." "%%~dpG%%~J" "%%~nxG" /Mov 1> NUL))))
I need a big help from the community, please if somebody can give me some hints. I have the following windows batch script which is supposed to read more than 10 million records as different CSV files and merge them all together. I am running the script on the server. So it's not very slow. But the problem is that the code doesn't handle duplicated records. I am not sure how to change the script in order to handle the duplication records and only passed unique records. I would be very very appreciated for your help.
rem Set current working directory to Task folder
set FilePath=%~dp0
set FolderPath=%FilePath:~0,-1%
rem Set Space environment variables
call "%FolderPath%"\..\SpaceEnv.bat
rem Set Task specific environment variables
set TaskName=MergeCSVfiles
set fileName=result.csv
set LogFile=%TaskName%_%LogDateTime%.log
:begin
cd ..
cd "Source Files\DCM_Source\Inbox"
echo Staring merge %fileName% at: %time%
setlocal enabledelayedexpansion
set "first=1"
>%fileName% (
for %%F in (msource*.csv) do (
if not "%%F"=="%fileName%" (
set /p "header="<"%%F"
if defined first (
type "%%F"
set "first="
) else (
type "%%F" |find /V "!header!"
)
)
)
)
endlocal
echo Finish merging %fileName% at: %time%
******UPDATED******
Example of CSV file
Sites|Level 2 sites|Date-time (visit start)|Visit ID|Unique visitor ID|Date-time (event)|Sources|Visitor categories|Visitor ID|Visits
SE Romania|PRM|2018-01-01T00:30:04|1|-6427177464|2018-01-01T00:30:04|Portal sites|-|0|2
SE Romania|PRM|2018-01-01T00:30:04|1|-6427177464|2018-01-01T00:30:04|Portal sites|-|0|2
This code will dedupe a file. In order to do that it must be sorted. This means any header record at the top of the file will be sorted into the file. This is code I received from dbenham. I can't remember if he originally posted it on StackOverflow or DosTips.com. If the file is very large it will more than likely crash with an out of memory error.
#echo off
:: Call function to dedupe file
CALL :DEDUPE "filename.txt"
goto :eof
:DEDUPE
:: DEDUPE file
setlocal disableDelayedExpansion
set "file=%~1"
set "sorted=%file%.sorted"
set "deduped=%file%.deduped"
::Define a variable containing a linefeed character
set LF=^
::The 2 blank lines above are critical, do not remove
sort "%file%" >"%sorted%"
>"%deduped%" (
set "prev="
for /f usebackq^ eol^=^%LF%%LF%^ delims^= %%A in ("%sorted%") do (
set "ln=%%A"
setlocal enableDelayedExpansion
if /i "!ln!" neq "!prev!" (
endlocal
(echo %%A)
set "prev=%%A"
) else endlocal
)
)
>nul move /y "%deduped%" "%file%"
del "%sorted%"
GOTO :EOF
#ECHO OFF
SETLOCAL
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filenamecommon=q49264647*.csv"
:: switch to required source directory
PUSHD "%sourcedir%"
:: get header line
FOR %%f IN (%filenamecommon%) DO FOR /f "delims=" %%h IN (%%f) DO SET "header=%%h"&goto gotheader
:gotheader
COPY %filenamecommon% atempfilename
SET "lastline="
>resultfilename (
ECHO %header%
SETLOCAL enabledelayedexpansion
FOR /f "delims=" %%d IN ('sort atempfilename' ) DO (
IF "%%d" neq "!lastline!" IF "%%d" neq "%header%" ECHO %%d
SET "lastline=%%d"
)
endlocal
)
DEL atempfilename
popd
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
I used file/dirctorynames that suit my system for testing.
Note : datafiles containing the characters ! or ^ or unbalanced " will not be processed correctly.
First, find the header line by setting header from any matching filename. Once header is set, forcibly abort the for loops.
copy and concatenate all of the required files to a tempfile.
output the header line, then sort the tempfile to group identical lines. Read the result and output only those lines that differed from the previous and were not header lines.
Applying /i to the if statements will make the entire routine disregard character-case.
Sort the tempfile
Ok. Give this code a try. I think this code would generate the result file with not duplicated records not matters its size. However, the time the program will take depends on several factors, although IMHO it should not be excessive because the core part of the process is based on findstr.exe command.
#echo off
setlocal
del result.csv 2>NUL
rem Process all input files
for /F "delims=" %%f in ('dir /B /O:-S msource*.csv') do (
echo Merging file: %%f
if not exist result.csv (
rem Initialize output file with first input file
copy "%%f" result.csv > NUL
) else (
rem Get records in this file that are not in result file
findstr /V /G:result.csv "%%f" > newRecords.csv
rem and add they to the result file
type newRecords.csv >> result.csv
)
)
del newRecords.csv
You may also try to eliminate the dash in /O:-S switch of dir command; perhaps this change will speed up the process a little...
I want a batch file which will find out which is the second latest folder created/modified in a directory.
I found this article but no matter how much i tried i could not understand how it works
#echo off
set "root_dir=c:\somewhere"
pushd "%root_dir%"
set "bl1="
set "bl2="
setlocal enableDelayedExpansion
for /f "tokens=* delims=" %%# in ('dir /b /a:-d /o:d') do (
set "bl2=!bl1!"
set "bl1=%%#"
)
echo %bl2%
endlocal
If i use it as it is then i can get the second latest folder but this script is supposedly able to get which ever latest folder you need , be it 1st or nth.
Could someone please tell me what modifications need to be done to the script to accomplish that. Also how exactly this script works
In your approach, the latest folder is already available in variable bl1; add echo %bl1% at the end before endlocal to display it. Retrieving the nth folder is simply not possible in a flexible way with that script as you would need to define another variable (say bl3, bl4,..., bln) within the loop.
However, you could reverse the sort order of the output of the dir command by changing the /O option, so it returns the latest (most recent) item first. Then let an index number count the iterations of the loop, and if that index equals the predefined number n, store the currently iterated item:
#echo off
setlocal EnableDelayedExpansion
rem // Define N here to get Nth-latest folder:
set /A LATEST=2
set /A INDEX=0
for /F "eol=| delims=" %%# in ('dir /B /A:D /O:-D "C:\somewhere"') do (
set /A INDEX+=1
if !INDEX! EQU !LATEST! (
set "ITEM=%%#"
)
)
if defined ITEM echo %LATEST%th-latest folder: %ITEM%
endlocal
exit /B
Update
Here is a modified script with the following improvements:
Exclamation marks ! in folder names are no longer lost due to toggling delayed expansion;
the target directory can be provided as the first command line argument; if omitted, the current directory is used;
the number n can be given as the second command line argument; if omitted, the user is prompted for it (this addresses elzooilogico's comment); n defaults to 1 for empty input;
the display output is improved to avoid something weird like 1th-latest, 2th-latest and 3th-latest; instead, The latest, 2nd-latest and 3rd-latest is returned, respectively;
So this is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* define location path and folder pattern as 1st command line argument;
rem /* Define number N as 2nd command line argument to get Nth-latest folder. */
set "LATEST=%~2"
set /A LATEST+=0
if %LATEST% LEQ 0 (set /P LATEST="Enter N [1]: " & set /A LATEST+=0)
if %LATEST% LEQ 0 set /A LATEST=1
set /A INDEX=0
for /F "eol=| delims=" %%# in ('dir /B /A:D /O:-D "%~1"') do (
set /A INDEX+=1
setlocal EnableDelayedExpansion
if !INDEX! EQU !LATEST! (
endlocal
set "ITEM=%%#"
goto :SKIP & rem // break loop after having retrieved Nth-latest folder;
) else endlocal
)
:SKIP
setlocal EnableDelayedExpansion
if defined ITEM (
if %LATEST% EQU 1 (echo The latest file: !ITEM!) else (
if %LATEST% EQU 2 (echo 2nd-latest file: !ITEM!) else (
if %LATEST% EQU 3 (echo 3rd-latest file: !ITEM!) else (
echo %LATEST%th-latest file: !ITEM!)))
)
endlocal
endlocal
exit /B
To achieve a similar result as with the simple script on top of this answer, you need to call this script by the following command line, supposing it has been saved as Nth-latest.bat:
Nth-latest.bat "C:\somewhere" 2
I got something that has been given me some problems for a little while. I have a list of reports that are .csv files. The way they are organized is:
Call Details Report_1448937644342.csv
Call Details Report_1449662976507.csv
Call Details Report_1450293169999.csv
Initial Call Pricing By Archive Report_1448937621469.csv
Initial Call Pricing By Archive Report_1449662916869.csv
Initial Call Pricing By Archive Report_1450293146194.csv
Location Detail Report_1448937658179.csv
Location Detail Report_1449662949955.csv
Location Detail Report_1450293201330.csv
Location Summary Report_1448937672801.csv
Location Summary Report_1449662994508.csv
Location Summary Report_1450293231606.csv
StartStop (1).csv
StartStop (2).csv
StartStop (3).csv
StartStop.csv
Sensor (1).csv
Sensor (2).csv
Sensor (3).csv
So what I would need is something that I can copy the most recent of each report to a different directory while renaming it without the spaces or numbers (CallDetailsReport, IntialCallPricingByArchiveReport, etc.). So if I would run the batch file now it would take that directory of files, find Most recent of each report, copy and rename it to another directory.
I have tried to use the FOR command, but have had very little luck, the biggest problem I have is the number after the _ varies greatly, but it is always greater. I also thought that maybe I could narrow it down by the most recent files, but the endings always being different is kind of messing me up. I am hoping you guys can help.
I got this so far that gives me a list, but does not narrow it down to the most recent.
FOR %%G IN (Report1*.csv ) do #echo %%G
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
FOR /f "tokens=1*delims= " %%a IN (
'dir /b /a-d /o-d "%sourcedir%\*.csv" '
) DO (
IF "%%b" neq "" IF NOT EXIST "%destdir%\%%a" COPY "%sourcedir%\%%a %%b" "%destdir%\%%a" >nul
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
Read the source directory in basic form without directorynames and in reverse-date order so the latest files matching the mask appear first. Split the filename into two using the space as a separator. If the second part is not empty (ie the space exists) test for the presence of a file thefirstpart in the destination directory and perform the copy if it doesn't exist - consequently it will be copied from the first (latest) file found and not be overwritten.
Adjustments to suit your actual requirement would be in your court.
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
FOR /f "tokens=1*delims=_" %%a IN (
'dir /b /a-d /o-d "%sourcedir%\*.csv" '
) DO (
IF "%%b" equ "" (
FOR /f "tokens=1*delims= " %%j IN ("%%a") DO (
IF "%%k" neq "" IF NOT EXIST "%destdir%\%%j" COPY "%sourcedir%\%%a" "%destdir%\%%j"
)
) ELSE (
IF NOT EXIST "%destdir%\%%a" COPY "%sourcedir%\%%a_%%b" "%destdir%\%%a"
)
)
GOTO :EOF
Revision to suit realistic filenames.
Try this:
#echo off
set max=0
for /f "tokens=2 delims=_." %%n in ('dir /b Report1*.csv') do (
if %%n GTR !max! set max=%%n
)
copy "Report1 Number_%max%.csv" otherdir\Report1.csv
Original Answer (based on the original question)
Assuming that the greatest numbers denote the most recent items, the following batch script does what you are looking for:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
rem Set up source and destination directories here:
set "SOURCE=D:\source"
set "DESTIN=D:\destin"
for /F "tokens=1,2,3,4 delims=._ " %%I in ('
2^> nul dir /B /A:-D "%SOURCE%\*.csv" ^| ^
findstr /I /R /C:"^Report[0-9][0-9]* Number_[0-9][0-9]*.csv$"
') do (
set "REPORT=%%I"
set "REPORT=0000000!REPORT:*Report=!"
set "NUMBER=0000000%%K"
set "ITEM-!REPORT:~-8!=%%I.%%L"
set "ITEMS-!REPORT:~-8!-!NUMBER:~-8!=%%I %%J_%%K.%%L"
)
for /F "tokens=2,3 delims=-=" %%I in ('
2^> nul set ITEM-
') do (
for /F "tokens=2 delims==" %%X in ('
2^> nul set ITEMS-%%I-
') do (
set "RECENT=%%X"
)
> nul copy /Y /B "%SOURCE%\!RECENT!" "%DESTIN%\%%J"
)
endlocal
exit /B
Basically this approach builds up array-like variables ITEM- and ITEMS- that hold the numbers in the file names padded with leading zeros to consist of 8 digits, then use set to sort the items alphabetically and retrieve the most recent item. Because of the padding, alphabetic sorting results in the same order as alphanumeric sorting.
Both of these variables are set up in the first for /F loop, which enumerates all the applicable items using dir /B /A:-D "*.csv" | findstr /I /R /C:"^Report[0-9][0-9]* Number_[0-9][0-9]*.csv$". findstr is used to filter the files, because dir cannot so that in the same grade of detail. The variable names contain the zero-padded numbers for proper sorting. The variable values are the original file names (ITEMS-) and the new file names (ITEM-).
The second for /F loop parses the output of set ITEM-, which walks through all the first numbers after the word Report. This loop nests another one, iterating through the output of set ITEMS-, which holds both numbers in the file names. The inner loop stores the current item in variable RECENT and overwrites its value each time. Due to the sort order, the greatest number and therefore the most recent item is stored in RECENT. The outer loop is then actually copying the relative file.
Relying on the sample files you provided, the two arrays will hold the following data:
ITEM- (sorted):
ITEM-00000001=Report1.csv
ITEM-00000002=Report2.csv
ITEM-00000003=Report3.csv
ITEMS- (sorted):
ITEMS-00000001-00000123=Report1 Number_123.csv
ITEMS-00000001-00000126=Report1 Number_126.csv
ITEMS-00000001-00000133=Report1 Number_133.csv
ITEMS-00000002-00000123=Report2 Number_123.csv
ITEMS-00000002-00000126=Report2 Number_126.csv
ITEMS-00000002-00000133=Report2 Number_133.csv
ITEMS-00000003-00000123=Report3 Number_123.csv
ITEMS-00000003-00000126=Report3 Number_126.csv
ITEMS-00000003-00000133=Report3 Number_133.csv
Updated Answer (based on the revised question)
After you changed your original requirements intensively in the most recent revision of your question, I have reworked the script extensively and came up with the following two scripts, each handling a certain name pattern of your *.csv report files. Both scripts rely on a temporary file which is used for appropriate sorting using the sort command, to get the correct items with the greatest numbers:
This script handles all your report files that have a report name, an underscore _ and an index number in their file names.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Set up source and destination directories here:
set "SOURCE=D:\source"
set "DESTIN=D:\destin"
> "%~dpn0.tmp" (
for /F "tokens=1,2 delims=_" %%I in ('
2^> nul dir /B /A:-D "%SOURCE%\*.csv" ^| ^
findstr /I /R /C:"^[^_][^_]*_[0-9][0-9]*.csv$"
') do (
set "REPORT=%%I"
set "NUMBER=00000000000000000000000%%~nJ"
setlocal EnableDelayedExpansion
echo(!REPORT!^|!NUMBER:~-24!^|!REPORT!_%%J
endlocal
)
)
set "FORMER="
< "%~dpn0.tmp" (
for /F "tokens=1,3 delims=|" %%I in ('
sort /R
') do (
set "REPORT=%%I"
set "ORNAME=%%J"
setlocal EnableDelayedExpansion
if /I not "!REPORT!"=="!FORMER!" (
> nul copy /Y /B "%SOURCE%\!ORNAME!" "%DESTIN%\!REPORT: =!%%~xJ"
)
endlocal
set "FORMER=%%I"
)
)
del /Q "%~dpn0.tmp"
endlocal
exit /B
The related temporary file contains the following unsorted data, based on your sample files:
Call Details Report|000000000001448937644342|Call Details Report_1448937644342.csv
Call Details Report|000000000001449662976507|Call Details Report_1449662976507.csv
Call Details Report|000000000001450293169999|Call Details Report_1450293169999.csv
Initial Call Pricing By Archive Report|000000000001448937621469|Initial Call Pricing By Archive Report_1448937621469.csv
Initial Call Pricing By Archive Report|000000000001449662916869|Initial Call Pricing By Archive Report_1449662916869.csv
Initial Call Pricing By Archive Report|000000000001450293146194|Initial Call Pricing By Archive Report_1450293146194.csv
Location Detail Report|000000000001448937658179|Location Detail Report_1448937658179.csv
Location Detail Report|000000000001449662949955|Location Detail Report_1449662949955.csv
Location Detail Report|000000000001450293201330|Location Detail Report_1450293201330.csv
Location Summary Report|000000000001448937672801|Location Summary Report_1448937672801.csv
Location Summary Report|000000000001449662994508|Location Summary Report_1449662994508.csv
Location Summary Report|000000000001450293231606|Location Summary Report_1450293231606.csv
There data is then sorted with sort /R, where /R defines reverse sort order. The first |-delimited field contains the report name, the second field the zero-padded index number and the third one the original file name. Only such lines are used for copying which hold a report name different to the previous line.
This script handles all your report files that have a report name, a SPACE, a (, an index number and a ) in their file names. It even handles files that do not contain an index number but a report name only in their file names, where they are treated as having an index of 0.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Set up source and destination directories here:
set "SOURCE=D:\source"
set "DESTIN=D:\destin"
> "%~dpn0.tmp" (
for /F "tokens=1,2,3 delims=()" %%I in ('
2^> nul dir /B /A:-D "%SOURCE%\*.csv" ^| ^
findstr /I /R /C:"^[^()]*[^()0-9].csv$" /C:"^[^()][^()]* ([0-9][0-9]*).csv$"
') do (
if "%%J"=="" (
set "REPORT=%%~nI"
set "NUMBER=000000000000000000000000"
setlocal EnableDelayedExpansion
echo(!REPORT!^|!NUMBER:~-24!^|!REPORT!%%~xI
endlocal
) else (
set "REPORT=%%I"
set "NUMBER=00000000000000000000000%%J"
setlocal EnableDelayedExpansion
echo(!REPORT:~,-1!^|!NUMBER:~-24!^|!REPORT!^(%%J^)%%K
endlocal
)
)
)
set "FORMER="
< "%~dpn0.tmp" (
for /F "tokens=1,3 delims=|" %%I in ('
sort /R
') do (
set "REPORT=%%I"
set "ORNAME=%%J"
setlocal EnableDelayedExpansion
if /I not "!REPORT!"=="!FORMER!" (
> nul copy /Y /B "%SOURCE%\!ORNAME!" "%DESTIN%\!REPORT: =!%%~xJ"
)
endlocal
set "FORMER=%%I"
)
)
del /Q "%~dpn0.tmp"
endlocal
exit /B
The related temporary file contains the following unsorted data, based on your sample files:
Sensor|000000000000000000000001|Sensor (1).csv
Sensor|000000000000000000000002|Sensor (2).csv
Sensor|000000000000000000000003|Sensor (3).csv
StartStop|000000000000000000000001|StartStop (1).csv
StartStop|000000000000000000000002|StartStop (2).csv
StartStop|000000000000000000000003|StartStop (3).csv
StartStop|000000000000000000000000|StartStop.csv
The sorting technique is the same as for the other script.
In order to get all the report files you want, you need to execute both scripts.
I need to move files from c:\prueba1 to c:\prueba99 but I don't know how to make a comparison betweeen all files in the source directory (c:\prueba99) to move all files in the directory with the exception of the last modified file in the directory. I know there is a wmic command with get InstallDate, LastModified, but I don't know the ms-dos syntaxis to asign a variable and compare it to know that one file readed is the last modified
I found an example:
for /f "delims=" %%A in ('wmic datafile where "drive = 'c:' and path='\\windows\\'"
get LastModified^,Name /format:table^|find ":"^|sort /r') do #echo %%A
And tried to modify it with no result because it appears to just list the datafile names but not the files themselves.
This is my modified version:
for /f "skip=1 delims=" %%A in ('wmic datafile where "drive = 'c:' and path='\\prueba1\\'"
get LastModified^,Name /format:table^|find ":"^| sort/r') do move (%%A) c:\prueba99
for /f "skip=1 delims=" %%a in ('dir /b /tw /o-d /a-d c:\prueba1\*.*'
) do move "c:\prueba1\%%a" "c:\prueba99"
dir command get the files in descending creation date order, so the first is the latest. for command iterates over this list, skipping the first one, moving the files to the target folder.
A way to ge the last modified file :
#echo off
set $path=c:\prueba99
for /f %%a in ('dir/b/a-d/o-d "%$path%"') do (
set $Last=%%a
goto:next)
:next
echo Last modified : [ %$Last% ]
This should work for you and it also allows you to discard as many NEWEST files as you want (for your case I've taken 1):
#echo off
setlocal
:: change the next two statements to match what you want
set srcdir=C:\prueba1
set tgtdir=C:\prueba99
if not exist %tgtdir%\*.* md %tgtdir%
set ctr=0
for /f "tokens=*" %%a in ('dir "%srcdir%" /o-d /b') do call :maybemove "%%a"
set /a ctr-=3
echo %~n0: %ctr% files were moved from %srcdir% to %tgtdir%
endlocal
goto :eof
::--------------------
:maybemove
:: increment counter and bypass the ONE newest file
set /a ctr+=1
if %ctr% leq 1 goto :eof
:: remove the double-quotes from the front and back of the filename
set fn=%~1
:: perform the move
move /y "%srcdir%\%fn%" "%tgtdir%"
goto :eof