Windows Shell Batch Script to copy the last 10 modified files - windows

I'm trying to create a Script to copy from a Directory the last 10 modified files.
I'm trying with this code:
for /F %%I IN ('DIR *.* /B /A:-D-H /O:-D') do (
copy /Y %%I C:\DestinationDir
)
This code copy ALL of them, how can I stop the "for" after 10th iteration?
====================
Second try to solve this.
I tried splitting it in two scripts, but "exit /b" (or only "exit") finish all the batch process:
FirstScript, the same code but trying to exit after copying the newest file:
for /F %%I IN ('DIR *.* /B /A:-D-H /O:-D') do (
copy /Y %%I C:\DestinationDir
exit /b
)
SecondScript, that loops 10 times calling the FirstScript:
for /l %x in (1, 1, 10) do (
FirstScript.bat
)

Here is a variant that works without delayed expansion and without using FIND or FINDSTR. I conditionally exit the loop when I intentionally divide by zero.
set cnt=10
for /f "eol=: delims=" %%F in ('dir /o-d /a-d-h /b') do (
copy /y "%%F" c:\destinationDir
2>nul set /a 1/(cnt-=1) || goto :break
)
:break

for /f "tokens=1,* delims=:" %%a in ('dir /o-d /a-d-h /b * ^| findstr /n "^"') do (
if %%a gtr 10 goto done
copy /y "%%b" c:\destinationDir
)
:done

Try this:
#echo off
setlocal enabledelayedexpansion
set cnt=0
for /f "tokens=1,2* delims=[]" %%b in (
'dir /b /a-d-h /o-d^|find /n /v ""') do (
set /a cnt+=1
if !cnt! LEQ 10 echo copy /y "%%c" "c:\DestinationDir"
)
remove the echo when you see desired results.

Related

Auto delete files in subfolders after a certain count value

Can anyone please explain to me how batch scripts work in Windows? I have a folder that contains subfolders, and those contain images. I need a script that automatically deletes images from 7 and up in these subfolders, just to leave a maximum of 6 images in each subfolder, ideally the first 6 alphabetically. I found a few scripts, saved them as .bat, but I have no idea what to do next.
1
#echo off
setlocal
set /a cnt=0
set "keep=6"
for /f "eol=: delims=" %%F in ('dir /b /o-d /a-d *.jpeg') do (
if defined keep (
2>nul set /a "cnt+=1, 1/(keep-cnt)" || set "keep="
) else del "%%F"
)
2
#echo off
for /f "tokens=1* delims=:" %%a in ('dir /b /o-d *.jpeg ^| findstr /N "^"') do (
if %%a gtr 6 del "%%b"
)
3
for /f "skip=7 eol=: delims=" %%F in ('dir /b /o-d /a-d *.jpeg') do #del "%%F"
4
#echo off
setlocal enableextensions disabledelayedexpansion
rem How many elements to keep
set "keep=6"
rem Retrieve folder from batch arguments
set "folder=%~1"
rem If no folder indicated, use current active directory
if not defined folder for %%a in (.) do set "folder=%%~fa"
rem Ensure we are working in the correct folder
pushd "%folder%" && (
rem For each element in the folder, skipping the first n
for /f "skip=%keep% delims=" %%a in (' dir /b /on ') do (
rem If it is a folder, use rmdir, else use del
if exist "%%a\" ( echo rmdir /s /q "%%a" ) else ( echo del "%%a" )
)
rem Work done. Return to previous active directory
popd
) || (
rem Folder change failed
echo Target folder does no exist
)
I finally found a script that works the way I need it to
for /f %%a in ('dir /b /ad') do (
for /f "skip=6" %%b in ('dir /b %%a /on') do (
del %%a\%%b
)
)

How to check out with Batch , if a folder contains a .txt file and then copy the folder into an other location?

I would like to make a script with Batch. I want from this script to check it out, If a folder contains a file, "list.txt", and if it is in the folder i want to make a copy in an other location. I wrote some lines of code but it's not working. Any ideas?
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:loop
for /f %%i in ('dir "C:\Users\ntosis\Desktop\Draft" /ad /o:d /s /b') do (
SET a=%%i
for /R %%a %%t in (*.txt) do if "%%~nxt"=="list.txt" SET p=%%~dpnxt
echo !p!
IF DEFINED %p% ( robocopy C:\Users\ntosis\Desktop\Draft\%a% C:\Users\ntosis\Desktop\Copied\%a% /MOVE /E )
)
echo Folder is empty or does not exist
timeout /t 15
goto loop
The problem in this moment is that the second loop does not the check right.
How about this:
for /f %%i in ('dir "C:\Users\ntosis\Desktop\Draft" /ad /o:d /s /b') do (
IF EXIST "%%i\list.txt" (
robocopy %%i C:\Users\ntosis\Desktop\Copied\ /MOVE /E
)
)
#echo off
setlocal enableextensions disabledelayedexpansion
set "source=C:\Users\ntosis\Desktop\Draft"
set "target=C:\Users\ntosis\Desktop\Copied"
for /l %%t in (0) do (
set "found="
for /d /r "%source%" %%a in (list.txt) do if exist "%%a" (
for %%b in ("%%~dpa.") do (
echo Found "%%a"
robocopy "%%~dpa." "%target%\%%~nxb" /move /e
)
set "found=1"
)
if not defined found (
echo folder is empty or does not exist
)
timeout /t 15
)

look for file of a certain type one subfolder down, sort by date modified (newest first), and display subfolder name (NOT filename) in sorted order

This is a very specific question, so I've separated it into 3 parts.
Look in all subfolders for a certain file: world.sav, but only 1 level down, i.e.
C:\workingdir\foo\world.sav
C:\workingdir\bar\world.sav
C:\workingdir\baz\world.sav
C:\workingdir\qux\world.sav
is fine, but
C:\workingdir\foo\bar\world.sav
C:\workingdir\world.sav
isn't, etc.
Sort these world.sav files by date modified (newest first).
Display the name of the subfolder each world.sav file is in, in the previously sorted order. i.e. if the above list was date-sorted into
C:\workingdir\qux\world.sav (newest)
C:\workingdir\bar\world.sav
C:\workingdir\foo\world.sav
C:\workingdir\baz\world.sav (oldest)
then the output would be
qux
bar
foo
baz
I've tried numerous methods involving DIR, FORFILES and manipulation of variables, but the 2 main problems I've come across so far are
Both commands mentioned above will arrange by subfolder, then by date, and I can't find a way to avoid it.
Date formatting. It appears to be different for every localization of Windows, and I really want this to be locale-independent.
Also, it must support spaces in folder names.
EDITED version with a wait message:
#echo off
echo Please wait...
cd /d "c:\workingdir"
(for /d %%a in (*) do #robocopy "%%a" "%%a" world.sav /L /nocopy /is /njh /njs /ndl /nc /ns /ts)|(
#cls
for /f "tokens=2*" %%b in ('sort /r') do #for %%z in ("%%~dpc\.") do #echo %%~nz
)
cd /d "%~dp0"
echo.
pause
original version below
This works here:
robocopy is used on each folder in the directory to generate a list (UTCdate time drv:\path\world.sav format) and that is sorted to get most recent at the top of the list, which the second for parses to extract the drv:\path\folder\world.sav and the last for loop prints out just the folder name.
#echo off
cd /d "c:\workingdir"
(for /d %%a in (*) do #robocopy "%%a" "%%a" world.sav /L /nocopy /is /njh /njs /ndl /nc /ns /ts)|for /f "tokens=2*" %%b in ('sort /r') do #for %%z in ("%%~dpc\.") do #echo %%~nz
pause
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET /a count=0
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" '
) DO IF exist "%sourcedir%\%%a\world.sav" CALL :set$ "%%a"
IF %count%==0 ECHO No files found&GOTO :EOF
IF %count%==1 ECHO %$1%&GOTO :EOF
:sortol
SET /a start=1
SET /a swap=0
:sortil
CALL :compdt
IF %start% neq %count% GOTO sortil
IF %swap% neq 0 GOTO sortol
FOR /l %%a IN (1,1,%count%) DO CALL ECHO %%$%%a%%
GOTO :EOF
:set$
SET /a count+=1
SET "$%count%=%~1"
GOTO :eof
:compdt
CALL SET f1=%%$%start%%%
SET /a START +=1
CALL SET f2=%%$%start%%%
FOR /f %%a IN (
'echo f^|xcopy /L /y /d "%sourcedir%\%f1%\world.sav" "%sourcedir%\%f2%\world.sav"'
) DO SET copies=%%a
IF %copies%==0 GOTO :EOF
SET /a swap +=1
SET /a START -=1
SET "$%start%=%f2%"
SET /a START +=1
SET "$%start%=%f1%"
GOTO :eof
This worked for me. You'd need to set sourcedir to suit your purpose.
Essentially, it builds a list of the next-level-down directory names which contain the target filename in variables $1...$whatever, then sorts the names using a simple bubble-sort which relies on the characteristics of xcopy. XCOPY's /L switch simply lists the files that would be copied; /y tells it to 'go ahead (and not copy)' and /d says 'only later files.ECHOingftellsxcopythat the desired "copy" is a file-copy, so it responds1 file(s) copiedor0...` as its last line, depending on whether the source file was later than the destination.
To reverse the order, simply change the if %copies%==0 to if %copies%==1
An idea :
#echo off
Setlocal EnableDelayedExpansion
set $WorkDir="C:\eh\guh\blugh\fneh\mork\workingdir"
for /f "tokens=*" %%a in ('dir /s/b/a-d %$WorkDir%') do (
for /f "tokens=9 delims=\" %%b in ('echo %%a') do (
if /i "%%b"=="world.sav" call:next "%%~pa" %%~ta
)
)
::Resultat
for /f "tokens=2 delims==" %%r in ('set #') do echo %%r
exit/b
:next
for /f "tokens=7 delims=\" %%c in ('echo %~1') do set $Path=%%c
for /f "tokens=1-3 delims=/" %%c in ('echo %~2') do set #%%e%%d%%c=!$path!

Building a directory listing via batch script. Need to exclude some directories. How?

I work with hundreds of thousands of folders/files, most of which have certain log files. It takes a lot of time to manually navigate through the folders to grab the logs, so Ive been using a script to do it.
Its a 2 parter. First part runs overnight to parse all of the directories (they change daily and new logs are added very often, so I index everything nightly to keep the index current).
This first part is automatic and I never touch it, it just runs nightly. Output goes to a txt file, which is used when I execute the second script.
Second script is where I put in keywords of what log I am looking for. This script relies on the txt output from the first script to quickly locate the file and pull it down.
(The reason I do it this way is because its a 5 second task to parse 300,000 line text file looking for a file, and its a 15-30 minute job to search (index all of the network shares). This way I can get my logs in 5 seconds and not 30+ minutes).
Prior to last week the log directories consisted of about 5000 files and folders. This past weekend IT switched over to a new system, and it is now consisting of over 500,000 files and folders. They dumped archives, and a bunch of other files in there that I do not need.
First script used to index it in about 10-15 minutes (its a network share, actually 4 network shares), no problem overnight. While now this is a 4+ hour feat. I need to be able to exclude certain directories during indexing thereby reducing the 500,000 file/folder count back down to under 5,000.
Ive been using this command to index:
dir /b /-d /-p /s /A:-D > C:\output.txt
I need have the indexing skip any directory containing the words "Common" or "Old" in them, and potentially others as well. The goal is to save time by not going inside of these directories at all.
I tried looking into PowerShell to do this, but I know nothing about it.
I need the output to be in the following format:
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\dbInstaller.exe
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\DisplayDriver.nvi
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\DisplayDriverExt.dll
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\license.txt
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\mcu.ex_
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvae.inf
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvak.inf
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvapi.dl_
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvapi64.dl_
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvcompiler.dl_
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvcompiler32.dl_
C:\NVIDIA\DisplayDriver\GeForce332.21Driver\Display.Driver\nvcplsetupeng.exe
Thanks!
#ECHO OFF
SETLOCAL
SET "sourcedir=."
(
FOR /f "delims=" %%a IN (
'dir /b /s /ad "%sourcedir%\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO ( FOR /f "delims=" %%b IN ('dir /b /a-d "%%a" 2^>nul') DO ECHO(%%a\%%b
)
)>newfile.txt
GOTO :EOF
I used a file named q22903564.txt containing exclusion words for my testing.
Produces newfile.txt.
Naturally, choice of sourcedir, q22903564.txt and newfile.txt are all in your court...
#ECHO OFF
SETLOCAL
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
SET "sourcedir=."
:: get a dir listing from the root
(
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO (ECHO("%%~fa")
)>"%tempfile%b"
:again
(
FOR /f "usebackqdelims=" %%a IN ("%tempfile%b") DO (
FOR /f "delims=" %%b IN (
'dir /b /ad "%%~a\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO FOR /f "delims=" %%c IN ('ECHO("%%~a\%%b"^|findstr /x /v /i /l /g:"%tempfile%b"') DO ECHO "%%~c"
)
)>"%tempfile%c"
FOR %%a IN ("%tempfile%c") DO SET /a sizec=%%~za
IF %sizec% gtr 0 TYPE "%tempfile%c">>"%tempfile%b"&GOTO again
(
FOR /f "usebackqdelims=" %%a IN ("%tempfile%b") DO (
FOR /f "delims=" %%b IN (
'dir /b /a-d "%%~a\*" 2^>nul'
) DO ECHO(%%~a\%%b
)
)>newfile.txt
DEL "%tempfile%*"
GOTO :eof
Well - need to charge you extra for doing what, according to some, is impossible.
#ECHO OFF
SETLOCAL
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
SET "sourcedir=."
:: get a dir listing from the root
(
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO (ECHO("%%~fa")
)>"%tempfile%b"
SET /a sizec=0
:again
SET "skipcnt=usebackq"
IF %sizec% gtr 0 (
FOR /f %%a IN ('type "%tempfile%b" ^|find /c /v ""') DO SET "skipcnt=usebackqskip=%%a"
TYPE "%tempfile%c">>"%tempfile%b"
)
(
FOR /f "%skipcnt%delims=" %%a IN ("%tempfile%b") DO (
FOR /f "delims=" %%b IN (
'dir /b /ad "%%~a\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO FOR /f "delims=" %%c IN ('ECHO("%%~a\%%b"^|findstr /x /v /i /l /g:"%tempfile%b"') DO ECHO "%%~c"
)
)>"%tempfile%c"
FOR %%a IN ("%tempfile%c") DO SET /a sizec=%%~za
IF %sizec% gtr 0 GOTO again
(
FOR /f "usebackqdelims=" %%a IN ("%tempfile%b") DO (
FOR /f "delims=" %%b IN (
'dir /b /a-d "%%~a\*" 2^>nul'
) DO ECHO(%%~a\%%b
)
)>newfile.txt
DEL "%tempfile%*"
GOTO :eof
Third time's a charm. This version works out how many lines of directory it's already processed and skips those lines when re-processing the concatenated lists b and c. IOW, it only processes the additions.
And in typing this, I've realised there's another little simplification... have to wait for morning tea, though...
#ECHO OFF
SETLOCAL
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
SET "sourcedir=."
:: get a dir listing from the root
(
FOR /f "delims=" %%a IN (
'dir /b /ad "%sourcedir%\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO ECHO(%%~fa
)>"%tempfile%c"
SET /a sizec=0
:again
SET "skipcnt=usebackq"
IF %sizec% neq 0 FOR /f %%a IN ('type "%tempfile%b" ^|find /c /v ""') DO SET "skipcnt=usebackqskip=%%a"
TYPE "%tempfile%c">>"%tempfile%b"
(
FOR /f "%skipcnt%delims=" %%a IN ("%tempfile%b") DO (
FOR /f "delims=" %%b IN (
'dir /b /ad "%%a\*" ^|findstr /v /i /l /g:q22903564.txt'
) DO ECHO(%%a\%%b
)
)>"%tempfile%c"
FOR %%a IN ("%tempfile%c") DO SET /a sizec=%%~za
IF %sizec% gtr 0 GOTO again
(
FOR /f "usebackqdelims=" %%a IN ("%tempfile%b") DO (
FOR /f "delims=" %%b IN (
'dir /b /a-d "%%a\*" 2^>nul'
) DO ECHO(%%a\%%b
)
)>newfile.txt
DEL "%tempfile%*"
GOTO :EOF
This time for sure! (no lion's roar thought)
I realised that the re-filtering against the original tempfileb content was redundant - and removing that meant that the quoting regime to overcome awkward characters could be scaled back. Then in reality the original file was the original addition (tempfilec) to an empty tempfileb, so - a little reorganisation and hand-waving (because 'skip=0' is invalid) and the result is that the only directories that are scanned later are thos that have beed added in the last iteration, optimising the directory-scan methodology.
Now the consequences are that the resultant list will be \a\, \b\, \c\, \a\a1\, \b\b1\, and so on - but a simple sort of the result should take care of that, if required.
This may help: it excludes the terms in line 3 where you see "Common Old"
It creates a list of the folders, filters out the excluded terms, and uses the remaining folders to create the list.
If you only want *.log *.txt files etc then it could be made more efficient again - otherwise remove the *.log *.txt terms in the batch file in line 7
#echo off
dir /b /ad /s /-p > "C:\output.txt"
findstr /v /i "Common Old" "C:\output.txt" >"C:\output2.txt"
del "C:\output.txt"
for /f "usebackq delims=" %%a in ("C:\output2.txt") do (
pushd "%%a"
for /f "delims=" %%b in ('dir *.log *.txt /b /-p /a-d') do >>"C:\output.txt" echo(%%a\%%b
popd
)

Batch script to loop over files while renaming them

I can use this to loop over files:
for %%a in ("%directory%\*.%extension%") do (
echo FILE: %%~fa
)
goto :eof
What if I also rename the files, like this (:rename is a subroutine that renames the files):
for %%a in ("%directory%\*.%extension%") do (
echo FILE: %%~fa
call :rename "%%~fa"
)
goto :eof
In that case, some of the files are renamed (and echoed) two or even three times. I think it's because after they are renamed, in some cases they are regarded as new files that also need to be looped through. However, this does not happen with all the renamed files.
How can I overcome this? I want every file to be renamed only once.
Solution:
:: remove quotes:
:: http://www.dostips.com/?t=Snippets.TrimQuotes
for /f "useback tokens=*" %%a in ('%extension%') do set extension=%%~a
for /f "useback tokens=*" %%a in ('%directory%') do set directory=%%~a
for /f "delims=" %%a in ('dir /b /a-d "%directory%\*.%extension%" ^| sort /r') do (
echo FILE: %directory%\%%a
call :renamingSubroutine "%directory%\%%a"
)
goto :eof
OR:
:: remove quotes:
:: http://www.dostips.com/?t=Snippets.TrimQuotes
for /f "useback tokens=*" %%a in ('%extension%') do set extension=%%~a
for /f "useback tokens=*" %%a in ('%directory%') do set directory=%%~a
for /f "delims=" %%a in ('dir /b /a-d /s /on "%directory%\*.%extension%"') do (
echo FILE: %%~fa
call :renamingSubroutine "%%~fa"
)
goto :eof
try this:
for /f "delims=" %%a in ('dir /b /a-d /s "%directory%\*.%extension%"^|sort /r') do (
echo FILE: %%~fa
call :rename "%%~fa"
)
goto :eof
BTW you shouldn't give batch functions the names of cmd commands (rename).

Resources