batch findstr to certain depth of subfolders - batch-file

I had the below code for searching through sub directories for 2 exe files:
#echo off & setLocal EnableDELAYedeXpansion
for %%d in (c) do if exist %%d: (
for /f "delims=" %%a in ('dir/b/s/x %%d:\autolog.exe %%d:\autorun.exe 2^>nul ^| findstr /V /C:".*\.*\.*\.*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\.*\.*" /C:".*\.*\.*\.*\*"') do (
set var=%%a;!var!
))
echo %1,!var!, >>C:\test.txt
exit
While it works search for all subfolder (by using /s), I would like to have result returns only if it is within 4 subfolder level (e.g. c:\sf1\sf2\sf3\autorun.exe should be a valid result, while c:\sf1\sf2\sf3\sf4\autorun.exe and any finding further down the tree should be opt out and not returning as a result).
I use all wildcard combination (* | .| .*) along with "\V" in attempt to achieve it but failed. Why does it won't work or if there are other smarter way doing it?
Thanks in advance

Heres is a sample to limit to fourth folder level, using regular expressions in the findstr terms:
#echo off
for /f "delims=" %%a in ('dir /ad /b /s ^| findstr \\.*\\.*\\.*\\ ^| findstr /v \\.*\\.*\\.*\\.*\\') do echo %%a
pause

I was inspired by this answer. ROBOCOPY is available since Vista, and it is a robust utility that does more than copying files.
e.g. The /L switch prevents it from copying; while /LEV allows you to copy only the top N levels of root, which eliminates one FINDSTR.
Golfed
#echo off
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=*" %%F in ('
ROBOCOPY "C:\." "C:\." autolog.exe autorun.exe /S /LEV:4 /IS /L /NS /NC /NDL /NJH /NJS^|FINDSTR \\.*\\.*\\.*\\
') do set "var=%%~fF;!var!"
echo %1,!var!,>>C:\test.txt
Formatted
#echo off
====SETLOCAL EnableDelayedExpansion EnableExtensions
set "list="
set ^"FORCMD=^
%__APPDIR__%ROBOCOPY.EXE C:\. C:\. autolog.exe autorun.exe^
/S %=non-empty Subdirectories=%^
/LEV:4 %=MAX 4 LEVels=%^
/IS %=Include Same files=%^
/L %=List only (don't copy)=%^
/NS %=No Size=%^
/NC %=No Class=%^
/NDL %=No Directory List=%^
/NJH %=No Job Header=%^
/NJS %=No Job Summary=%^
|%__APPDIR__%FINDSTR.EXE \\.*\\.*\\.*\\%=MIN 4 LEVels=%" It's convenient to use delayed expansion
FOR /F "tokens=*" %%F in ('!FORCMD!') do set "var=%%~fF;!var!"
::Due to weird expansion rules
::if VAR was undefined, it is set to '~,-1'
if DEFINED var set "var=!var:~,-1!" Remove trailing ;
>>"C:\test.txt" echo(%1,!var!,

Related

batch file to Delete X days old files exclude few files

I want to delete X days old files from a directory but want to exclude a specific file (*exception). I am using below command, but seems it is not working for me.
for /f "delims=" %%a in ('forfiles /p "%userprofile%\.." /d -180 /c "cmd /c if #isdir==TRUE echo #file"^|findstr /vig:"%userprofile%\exception.txt"') do echo rd /s /q "%%~a"
Instead of using ForFiles you could use RoboCopy:
#For /F "Tokens=*" %%A In ('RoboCopy "%UserProfile%\.." Null /L /MinAge:180 /NC /NDL /NJH /NJS /NP /NS /XF exception*') Do #Del "%%A"
Enter RoboCopy /? at the Command Prompt for its usage information.
After re-reading your question, together with your code, it isn't clear whether you're wanting to exclude files named, exception*, which is what my answer above does, or to exclude files as listed inside a file named exception.txt. If it's the latter then you'd need to build a string of your exclusions first to append to the /XF list.
If your file only contains a few relatively short exclusion masks then you may be able to do it like this:
#Echo Off
Set "EX="
For /F "UseBackQ Delims=" %%A In ("%UserProfile%\exception.txt") Do (
If Not Defined EX (Set "EX=%%A ") Else Call Set "EX=%%EX%%%%A ")
For /F "Tokens=*" %%A In ('RoboCopy "%UserProfile%\.." Null /L /MinAge:180 /NC /NDL /NJH /NJS /NP /NS /XF %EX%') Do Del "%%A"

Why the loop is not running as expected?

I have folders like E:\Backups\code\Hazard\test1 ... testn
And inside these test folders something like E:\Backups\code\Hazard\test1\it0 ... itn
The root folder is E:\Backups\code from where the code runs.
The below code runs on each subfolders and copies summary.yml from it0 folder to latest it(n) folder.
Why the code runs just for test1 folder and then hangs?
setlocal ENABLEDELAYEDEXPANSION
set root=%cd%
for /D %%X in (%root%\*) do (
echo %%X
cd %%X
for /D /r %%b in (*) do (
cd %%b
echo %%b
for /f "tokens=1,2,*" %%a in ('robocopy . . file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs ^| sort /r') do set "lastFolder=%%~dpc" & goto :done
:done
echo Last folder : %lastFolder%
for /d %%j in (*) do (
if /i "%%~nj"=="it0" COPY %%j\summary.yml %lastFolder%
)
cd ..
)
)
There are two main problems in the code:
If goto is used inside a for loop, the loop is cancelled
If you set a variable inside a block of code (code inside parenthesis), to retrieve the value of the variable inside the same block of code you need delayed expansion, enabling it with setlocal enabledelayedexpansion and changing the syntax used to retrieve the value in the variable from %var% into !var!.
But
the goto can be removed as indicated in the previous answer,
the delayed expansion is not needed. Instead of storing the value from the for replaceable parameter inside a variable, just use the replaceable parameter
Not tested, but more or less
#echo off
setlocal enableextensions disabledelayedexpansion
rem E:\Backups\ code \ Hazard \ test1 \ it0 ... itn
rem ^root ^ %%X ^ %%Y ^ %%~dpc
for /D %%X in ("*") do for /D %%Y in ("%%~fX\*") do for /f "tokens=1,2,*" %%a in ('
robocopy "%%~fY." "%%~fY." file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs
^| sort /r 2^>nul
^| cmd /q /v /c "(set /p .=&echo(!.!)"
') do copy "%%~fY\it0\summary.yml" "%%~dpc."
Being E:\Backups\code the current active directory:
%%X will enumerate the folders under E:\Backups\code (Hazard)
%%Y will enumerate the folders under E:\Backups\code\Hazard (testn)
%%a executes a robocopy command to locate the folder containing the latest file.txt file
sort /r sorts the list of files in descending order so the latest file is the first in the list
cmd retrieves and outputs only the first line
With all the information available in the several for replaceable parameters, execute the indicated copy command.
I'm not sure about what the line with robocopy should do. It looks like this command is for getting name of last subdirectory in current directory.
Perhaps this code works better. But I could not test it.
setlocal EnableDelayedExpansion
set "root=%cd%"
for /D %%X in ("%root%\*") do (
echo %%X
cd "%%~X"
for /D /r %%b in (*) do (
cd "%%~b"
echo %%b
call :GetLastFolder
echo Last folder : !lastFolder!
for /d %%j in (*) do (
if /i "%%~nj"=="it0" copy "%%j\summary.yml" "!lastFolder!"
)
cd ..
)
)
goto :EOF
:GetLastFolder
for /f "tokens=1,2,*" %%a in ('robocopy . . file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs ^| sort /r') do set "lastFolder=%%~dpc" & goto :EOF
goto :EOF
It is at least necessary to reference environment variable lastFolder with exclamation marks instead of percent signs to really use delayed expansion as needed here.
goto :EOF exits the subroutine resulting in continuing on line below call :GetLastFolder.
There is one more goto :EOF or alternatively exit /B after main code necessary to avoid that the code of the subroutine is executed once more after most outer for loop finished. This goto :EOF results in exiting the processing of this batch file.
For understanding the used commands and how they work, open a command prompt window, execute there at least the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
for /?
goto /?
set /?

find path of latest file using batch command

I have files named file.txt in multiple subfolders in my folder. I want to get the path of the latest file.txt
FOR /r %%i IN ('DIR file.txt /B /O:-D') DO SET a=%%i
echo Most recent subfolder: %a%
gives latest created folder having file.txt whereas I want the folder which has latest file.txt
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2,*" %%a in ('
robocopy . . file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs
^| sort /r 2^> nul
^| cmd /v /q /c "set /p .=&echo(^!.^!"
') do (
echo File found : %%c
echo File timestamp (UTC^) : %%a %%b
echo Folder of file : %%~dpc
)
This will use the robocopy command to enumerate all the file.txt files under the current active directory, without copying anything but generating a list of all matching files with a yyyy/mm/dd hh:nn:ss utc timestamp. Then the list is sorted on the timestamp in descending order and only the first line in the output readed and echoed to be processed by the for /f tokenizer and retrieve the date, time and file with full path.
If only the folder is required and the list of files is not very large, a simplified version could be
#echo off
setlocal enableextensions disabledelayedexpansion
for /f "tokens=1,2,*" %%a in ('
robocopy . . file.txt /l /nocopy /is /s /nc /ns /ts /ndl /njh /njs
^| sort /r
') do set "lastFolder=%%~dpc" & goto :done
:done
echo Last folder : %lastFolder%
Almost the same, but instead of including a filter in the list generation to only retrieve the first line (required if the list of files is very large), here the for /f will retrieve the full list but after the first element is processed we jump out of the loop to the indicated label.

File Retention Script

I have a directory that has a 10 sub-directories. Each of these holds different .bak files. I'm trying to create a script that will check to see if X number of files are there and if the number of files exceeds X, it deletes the oldest file. In other words, I want 20 iterations of a .bak file. When the 21st one shows up, I want the batch file to delete the oldest one.
Is this possible?
If so, can I create a single script that looks in all the sub-directories?
Thanks in advance.
Two options included. The first one will leave %maxFiles% bak files under each of the folders. The second one (windows Vista or later OS is required as robocopy is used to obtain the sorted list of files) will leave %maxFiles% in total
#echo off
setlocal enableextensions disabledelayedexpansion
set "rootFolder=%cd%"
set "maxFiles=20"
rem Option 1 - Keep %maxFiles% inside each of the subfolders
for /d /r "%rootFolder%" %%z in (*) do for /f "skip=%maxFiles% delims=" %%a in (
'dir /tc /o-d /a-d /b "%%~fz\*.bak" 2^>nul'
) do echo del "%%~fz\%%~nxa"
echo ------------------------------
rem Option 2 - Keep %maxFiles% in total under all the subfolders
for /f "skip=%maxFiles% tokens=2,*" %%a in ('
robocopy "%rootFolder%" "%rootFolder%" *.bak /l /nocopy /is /s /njh /njs /ndl /nc /ns /ts
^| findstr /v /r /e /i /c:"%rootFolder:\=\\%\\[^\\]*"
^| sort /r
') do echo del "%%b"
del commands are only echoed to console. If the output is correct, remove the echo command to remove the files
assumes that you want to check the number of files from the parent directory.Can be done also for each sub-directory.
#echo off
setlocal
set "max_number_files=20"
set "parrent_dir=c:\whatever_you_need"
set "extension=.bak"
pushd "%parrent_dir%"
set "count=0"
setlocal enableDelayedExpansion
for /f "delims=" %%a in ('dir /s /b /a:-d /o:-d /t:c *%extension%') do (
set /a count=count+1
if 1!count! GTR 1!max_number_files! (
rem --- remove the echo to activate deletion
echo del /q /f "%%~a"
)
)
popd
endlocal
endlocal
This will check each folder under d:\base\folder and if there are more than 20 *.bak files it will remove the oldest ones so only 20 *.bak file remain, in each folder.
Test it on some sample folders.
#echo off
for /d /r "d:\base\folder" %%a in (*) do (
pushd "%%a"
for /f "skip=20 delims=" %%b in ('dir /b /a-d /o:-d *.bak ') do del "%%b"
popd
)

Finding the newest file in a directory

I am a complete novice in batch programming but have found some great scripts in here that I tried modifying. I need the info on the last file modified in a directory. The script below gives me a file with info about file name and modification time. It searches through subdirectories too but seems to get stuck in a subdirectory instead of finding the newer file in the parent directory. I am not sure what could be wrong (as I only partly understand the code). Any suggestions from you smart guys in here?
Thanks in advance!
#echo off
setlocal
set srcDir=C:\Test
set lastmod=
pushd "%srcDir%"
for /f "tokens=*" %%a in ('dir *. * /b /od /s /a-d 2^>NUL') do set lastmod=%%a
if "%lastmod%"=="" echo Could not locate files.&goto :eof
for /d %%a in ("%lastmod%") do echo "%lastmod%", Modified date: %%~ta>"C:\Test\Details.txt"
This uses robocopy so it will only work on windows Vista and later. For it to work on XP you will need to get a copy of robocopy from a later OS or from resource kit.
No copy operation will really be made, but it will allow to retrieve a recursive file list with an adequated file stamp that can be sorted to find latest file.
#echo off
setlocal enableextensions disabledelayedexpansion
set "folder=%cd%"
for /f "tokens=2,*" %%a in (
'robocopy "%folder%" "%folder%" "*" /s /is /nocopy /nc /ns /ts /fp /np /ndl /njh /njs /xjd /r:0 /w:0 /l ^| sort /r '
) do ( set "latest=%%b" & goto :done )
:done
for %%f in ("%latest%") do echo(%%~tf %%~ff

Resources