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.
Related
I am trying to loop through one directory and pic out the sub directory, which may be buried within other directories, and pull out the directory that has been most recently created. I have a loop here which will go into the root directory, which is set, and look at only directories, incorporate all sub directories, sort them by creation date, and have the loop set the one at the end of the list be the most recent which is then echoed out. However, for some reason the sorting does not happen correctly. It keeps pulling out a directory that is not the most recent. I cannot seem to pinpoint the issue. What could be causing this? Am I using the sorting correctly? Does it not compare sub directories to other directories on other levels? Any help would be greatly appreciated.
#ECHO OFF
SET dir=C:\Users\Darren\Google Drive\
echo %dir%
FOR /F "delims=" %%i IN ('dir "%dir%" /b /s /ad-h /t:c /od') DO (
echo %%i
SET a=%%i)
SET sub=%a%
echo Most recent subfolder: %sub%
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q43579378.txt"
SET "filename2=%sourcedir%\q43579378_2.txt"
(
FOR /f "delims=" %%a IN (
'dir /b /s /ad "%sourcedir%\*" '
) DO ECHO %%~a
)>"%filename1%"
IF EXIST "%filename2%" (
FOR /f "delims=" %%a IN ('findstr /L /V /i /x /g:"%filename2%" "%filename1%"') DO (
FOR /f %%c IN ('dir /b /a-d "%%a\*" 2^>nul^|find /c /v "" ') DO (
IF %%c gtr 20 ECHO(send email directory %%a has %%c files
)
)
)
MOVE "%filename1%" "%filename2%" >nul
GOTO :EOF
I used a test directory as sourcedir. Naturally, it doesn't have to be the same as the directory you're scanning, and the filenames can be whatever you like.
The first for builds a list of the absolute dirctorynames currently existing into filename1 (the "spare" pair of parentheses enclosing the for allows the redirection)
If filename2 exists (which will be for every run after the first), then find the difference between filename1 and filename2, using /L literal, /v doesn't match /i case-insensitive /x exact match /g: strings in this file and assign each new directoryname to %%a.
Then scan the directory of "%%a", files only, suppressing file not found messages and counting the number of returned lines (/c count /v lines not matching "" an empty string). Assign the re4sult to %%c, test and choose where to take action.
the carets ^ before the redirectors in the command-to-be-executed of the for ... %%c command escape the redirectors so that they are interpreted as part of the command-to-be-executed, not the for.
The dir /S /O command sorts the content of every sub-directory individually, and there is no way to change that behaviour.
A possible alternative approach is to use the wmic command, which is a bit slow but capable of deriving the date information (creation, last modification, last access) in a standardised, sortable, region- and locale-independent format:
wmic FSDir where Name="D:\\Data" get CreationDate /VALUE
wmic FSDir where (Name="D:\\Data") get CreationDate /VALUE
So below are two scripts that use a for /D /R loop to get all the (sub-)directories, the above wmic command lines, for /F to capture their output and a simple command to do the sorting by age.
The first one creates a temporary file to collect all directories, together with their creation dates, in the following format:
20170424215505.000000+060 D:\Data
For sorting, the sort command is used. Here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~1" & rem // (use first command line argument as the root directory)
set "_PATTERN=*" & rem // (search pattern for directories; `*` matches all)
set "_TMPF=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (specify a temporary file)
rem // Write list of directory paths preceded by their creation dates to temporary file:
> "%_TMPF%" (
set "ERR=0"
rem // Enumerate all matching directories recursively:
for /D /R "%_ROOT%" %%D in ("%_PATTERN%") do (
rem // Store currently iterated directory path:
set "DIRPATH=%%~D"
rem // Toggle delayed expansion to avoid trouble with the exclamation mark:
setlocal EnableDelayedExpansion
(
rem /* Capture `wmic` output to query creation date of currently iterated
rem directory in locale-dependent and sortable format: */
for /F "tokens=2 delims==" %%L in ('
rem/ Do two attempts, because one approach can handle `^)` and one can handle `,`; ^& ^
rem/ note that `wmic` cannot handle paths containing both of these characters: ^& ^
2^> nul wmic FSDir where Name^="!DIRPATH:\=\\!" get CreationDate /VALUE ^|^| ^
2^> nul wmic FSDir where ^(Name^="!DIRPATH:\=\\!"^) get CreationDate /VALUE
') do (
rem // Do nested loop to avoid Unicode conversion artefacts (`wmic` output):
for /F %%K in ("%%L") do echo(%%K !DIRPATH!
)
) || (
rem // This is only executed in case a path contains both `)` and `,`:
>&2 echo ERROR: Could not handle directory "!DIRPATH!"^^!
set "ERR=1"
)
endlocal
)
)
rem /* Return content of temporary file in sorted manner using `sort` command,
rem remember last item of sorted list; clean up temporary file: */
for /F "tokens=1*" %%C in ('sort "%_TMPF%" ^& del "%_TMPF%"') do set "LASTDIR=%%D"
rem // Return newest directory:
echo "%LASTDIR%"
endlocal
exit /B %ERR%
The second one stores each directory in a variable named of the creation date, in the following format:
$20170424215505.000000+060=D:\Data
For sorting, the set command is used. Here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~1" & rem // (use first command line argument as the root directory)
set "_PATTERN=*" & rem // (search pattern for directories; `*` matches all)
set "ERR=0"
rem // Clean up variables beginning with `$`:
for /F "delims==" %%C in ('2^> nul set "$"') do set "%%C="
rem // Enumerate all matching directories recursively:
for /D /R "%_ROOT%" %%D in ("%_PATTERN%") do (
rem // Store currently iterated directory path:
set "DIRPATH=%%~D"
rem // Toggle delayed expansion to avoid trouble with the exclamation mark:
setlocal EnableDelayedExpansion
(
rem /* Capture `wmic` output to query creation date of currently iterated
rem directory in locale-dependent and sortable format: */
for /F "tokens=2 delims==" %%L in ('
rem/ Do two attempts, because one approach can handle `^)` and one can handle `,`; ^& ^
rem/ note that `wmic` cannot handle paths containing both of these characters: ^& ^
2^> nul wmic FSDir where Name^="!DIRPATH:\=\\!" get CreationDate /VALUE ^|^| ^
2^> nul wmic FSDir where ^(Name^="!DIRPATH:\=\\!"^) get CreationDate /VALUE
') do (
rem // Do nested loop to avoid Unicode conversion artefacts (`wmic` output):
for /F %%K in ("%%L") do (
rem /* Assign currently iterated path to variable named of the
rem respective creation date preceded by `$`: */
endlocal & set "$%%K=%%~D"
)
)
) || (
endlocal
rem // This is only executed in case a path contains both `)` and `,`:
>&2 echo ERROR: Could not handle directory "%%~D"!
set "ERR=1"
)
)
rem /* Return all variables beginning with `$` in sorted manner using `set` command,
rem remember last item of sorted list: */
for /F "tokens=1* delims==" %%C in ('2^> nul set "$"') do set "LASTDIR=%%D"
rem // Return newest directory:
echo "%LASTDIR%"
endlocal
exit /B %ERR%
I have a directory of academic papers that were named using the convention below:
Author1-(Year)-Title.pdf
For example,
Jones-(2011)-XXX.pdf
Smith-(2002)-YYY.pdf
Johnson-(2015)-ZZZ.pdf
I would like to rename them as
(2011)-Jones-XXX.pdf
(2002)-Smith-YYY.pdf
(2015)-Johnson-ZZZ.pdf
That is, to extract the year from the file name and put it in front.
I tried the following code, which did not work
Setlocal enabledelayedexpansion
Set "Year=2013"
Set "Replace="""
For %%a in (*.pdf) Do (
Set "NewName=(%year%)-%%~a"
Ren "%%a" "%NewName%-File:%Year%=%Replace%!"
)
Pause&Exit
In case XXX also contains hyphens I'd suggest using tokens=1,2* to stop parsing the remainder of the file name.
I'd also remove the parentheses, when the year is first place there is no need to further emphasize it.
#Echo off
for /f "tokens=1-2* delims=-()" %%A in (
'Dir /b "*-(*)-*.pdf"'
) do Ren "%%A-(%%B)-%%C" "%%B-%%A-%%C"
Sample output
> dir /b
2002-Smith-YYY.pdf
2011-Jones-XXX.pdf
2015-Johnson-ZZZ.pdf
Not tested
for /f "tokens=1,2,3 delims=-" %%a in ('dir /b "*.pdf"') do (
echo ren "%%a-%%b-%%c" "%%b-%%a-%%c"
)
this will only echo the intended rename command.If it looks ok remove the echo word.
Derived form this SO article - it works:
#ECHO OFF &SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%x IN (*.pdf) DO (
FOR /f "tokens=1-3 delims=-" %%a IN ("%%~x") DO (
SET "Author=%%a"
SET "Year=%%b"
SET "Title=%%c"
ren %%x !Year!-!Author!-!Title!
)
)
Here is a reliable way of doing what you are asking for even if the author part contains - on its own. The title portion may even contain ( and ), but the author part must not. So this is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILES=.\*-(????)-*.pdf" & rem // (basic pattern to match correct files)
set "_REGEX=^[^()][^()]*-([0123456789][0123456789][0123456789][0123456789])-.*\.pdf$" ^
& rem // (precise filter to exclude files violating the demanded pattern)
if not defined _REGEX set "_REGEX=.*" & rem // (avoid trouble with empty filter)
rem // Loop through all matching files:
for /F "eol=: tokens=1,2,* delims=()" %%F in ('
dir /B /A:-D "%_FILES%" ^| findstr /I /R /C:"%_REGEX%"
') do (
rem // Store the extracted file name parts:
set "LEFT=%%F" & set "YEAR=%%G" & set "REST=%%H"
setlocal EnableDelayedExpansion
rem // Reassemble the name and rename the file:
ECHO ren "!LEFT!(!YEAR!)!REST!" "(!YEAR!)-!LEFT!!REST:*-=!"
endlocal
)
endlocal
exit /B
After having verified the correct output, remove the upper-case ECHO command to actually rename files.
I have the directory structure like this A->B->C,D->E,F Where A contains B folder and some text files,sql files,log files etc. B contains C folder and many other extension files.
Similarly E,F are also sub-directories and contains some files.
Now the output should be something like this:
Count of files:
A-10
A/B-15
A/B/C-20
D-25
D/E-30
F-12
Thanks for your response.All approaches are appreciated.
Have tried:
#ECHO OFF
SET "rootpath=%~1"
FOR /D %%D IN ("%~dp0*") DO (
SET cnt=0
FOR /F %%K IN ('DIR /A-D /S "%%D" 2^>NUL ^| FIND "File(s)" ^|^| ECHO 0') DO ( SET /A cnt+=%%K )
SETLOCAL EnableDelayedExpansion
ECHO %%D: !cnt!
ENDLOCAL
pause
)
You need a recursive subroutine that traverse the entire tree and perform the same calculation in each subfolder. This approach is simpler:
EDIT: Code modified to avoid errors when subfolder names have spaces.
#echo off
setlocal EnableDelayedExpansion
set "base=%cd%\"
rem Accumulate files in the same array element given by its folder
for /R %%a in (*.*) do (
set "folder=%%~DPa"
set "folder=!folder:%base%=!"
set /A "count[!folder: =_!]+=1" 2> nul
)
rem Show the result
for /F "tokens=2,3 delims=[]=" %%a in ('set count[') do (
echo %%a-%%b
)
For further description of array management in Batch files, see: Arrays, linked lists and other data structures in cmd.exe (batch) script
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.
I am attempting to use the batch file below to move files from one folder to another. The batch commands will create sub folders within the destination folder based on the create dates against each file in the source folder.
The problem is that the source folder contains sub folders and the batch commands cannot recurse into sub folders.
Please advise how to modify the batch file to allow recurse into sub folders on the source folder.
Thanks
Rialet
echo %1 "-" %2
If [%1]==[] ECHO "Source Directory parameter required"&GOTO :EOF
If [%2]==[] ECHO "Target Directory parameter required"&GOTO :EOF
SET TrimQuote=%2
for /f "useback tokens=*" %%T in ('%2') do set TrimQuote=%%~T
REM echo %TrimQuote%
::loop through files only
For /F "TOKENS=1 DELIMS=%_TabSpace%" %%B In ('dir %1 /a-d /B /OD') DO (
REM echo "%%B - " %%B
For /F "TOKENS=1 DELIMS=%_TabSpace%" %%D In ('dir %1\"%%B" /a-d /OD ^| findstr /B [0-9][0-9]/[0-9]') DO (
REM echo "%%D - " %%D
for /F "tokens=1,2,3,4 delims=/ " %%b in ("%%D") do (
REM echo "b = " %2\%%c%%a\%%b
REM echo %2\%%d%%c\%%b
if NOT exist %2\%%d%%c\%%b md %2\%%d%%c\%%b
move %1\"%%B" %2\%%d%%c\%%b\
)
)
)
Try using
For /F "TOKENS=1 DELIMS=%_TabSpace%" %%B In ('dir %1 /a-d /S /B /OD') DO (
the /s will cause recursion. The downside is that the output of the dir command is then d:\path\file.ext - which may not marry well with your "TOKENS=1 DELIMS=%_TabSpace%". You'd probably need to use "delims=" (ie, no delimiters, hence entire line in token1).
You can then retrieve the various parts of the full-filename as %%~dB, %%~pB, %%~nB and %%~xB (the drive, path, naem and extension - and you can combine these parts if you wish by using %%~nxB for name+extension, for instance.
Supplemental info - batch commented.
#ECHO OFF
SETLOCAL
:: The above two lines are a traditional batch introduction.
:: The first turns `ECHO`ing of the command to the console OFF
:: The second makes all changes to the environment 'local'
:: which means that any variable changes made during the batch
:: will be undone at the end, restoring the original environment.
:: Note that the official remarks/comments method is
REM This is a remark
:: But the double-colon method is commonly used as :: is less intrusive
:: Echo the two parameters given to the batch (%1 and %2)
echo %1 "-" %2
:: The original parameter-present detection is weak. This is a better method
SET target=%~1
If not defined target ECHO "Source Directory parameter required"&GOTO :EOF
SET target=%~2
If not defined target ECHO "Target Directory parameter required"&GOTO :EOF
:: Note `trimquote` (batch is largely case-insensitive) is a meaningless name
:: New name TARGET is better. Setting to %~2 removes enclosing quotes from
:: string assigned to variable.
::loop through files only
:: `"delims="` means there are no delimiters, so the entire line is assigned to
:: the variable `%%B` (FOR loop variablenames ("metavariables") ARE case-sensitive!)
:: The line being assigned comes from the output of the `DIR` command
:: which is filenames only (/a-d) in subdirectories (/s) in basic form (/b)
:: (ie name only, no dates, sizes, headers or summary) and in order of date (/od)
For /F "DELIMS=" %%B In ('dir "%~1" /a-d /S /B /OD') DO (
REM echo "%%B - " %%B
REM within a FOR loop, better to use REM remarks than :: remarks (version-dependent)
REM I believe the intention of the original here was to pick up the filedate
REM It wouldn't work since FINDSTR is looking for lines that begin (/B) with
REM 2 digits, a slash and one digit, but the date format about to be processed...
REM For /F "TOKENS=1 DELIMS=%_TabSpace%" %%D In ('dir %1\"%%B" /a-d /OD ^| findstr /B [0-9][0-9]/[0-9]') DO (
REM echo "%%D - " %%D
REM Process date - 4 elements separated by space or /. Pick the last three
REM so implictly format is DAYNAME xx/yy/zz BUT the elements would be applied
REM to %%b, %%c, %%d, %%e
REM for /F "tokens=1,2,3,4 delims=/ " %%b in ("%%D") do (
for /F "tokens=1,2,3,4 delims=/ " %%a in ("%%~tB") do (
REM echo "b = " %2\%%c%%a\%%b
REM echo %2\%%d%%c\%%b
REM Make a new directory. 2>nul suppresses error message if already exists
md "%TARGET%\%%d%%c\%%b" 2>nul
move "%%B" "%TARGET%\%%d%%c\%%b\"
)
)
Bit of a nightmare really - No idea of what format date you are using, nor what format target directory structure you want. This should "flatten" the structure, so any file filename.ext would be placed in %target%\xx\yy\zz regardless of where in your source structure the file originally resides. There is also no protection about multiple instances of the same filename.ext with the same DATE but in different subdirectories in the source. Need a lot more clarification of the entire scenario to be more certain. Really just commenting and changing the existing (presumed-working but evidently-faulty) batch...
You can also use the XCOPY function to copy a parent and child folders.