FORFILES in FOR loops issues - batch-file

i am trying to make a script to remove empty folders and delete files a number of days old. depending on what the txt file delimiters are set to. I have came up with this so far:
::Batch
SET CDID=%~dp0
SET TEST=TRUE
IF %TEST%==TRUE (
SET COMND1=ECHO
SET COMND2=ECHO
) ELSE (
SET COMND1=DEL
SET COMND2=RD
)
ECHO FILE RAN %date:~10%/%date:~4,2%/%date:~7,2% >>%CDID%\LOG.TXT
FOR /F "usebackq delims=| tokens=1,2" %%x IN (%CDID%PATH.txt) DO (
CALL :DEL_FOLDERS "%%x" %%y
CALL :DEL_FILES "%%x" %%y
)
GOTO :EOF
:DEL_FILES
FORFILES /p %1 /s /m *.* /d %2 /c "cmd /c %COMND1% #file"
GOTO :EOF
:DEL_FOLDERS
FOR /f "delims=" %%i in ('dir %%1 /s /b /ad ^| sort /r') do %COMND2% "%%i"
GOTO :EOF
::PATH.txt
C:\Temp\BLANK|10
C:\Temp\New folder|30
when i run the script #file will not populate and %%i will not populate, i am not sure what i am doing wrong. Help?

You made a couple of very small errors. In DEL_FOLDERS you used %%1 which meant that the argument was not expanded (you only needed one % here). You also did not handle the case where there are no files that match or the directories are empty. In the FORFILES command you put /m *.*; although the documentation says this is the default, the documentation is incorrect. Missing out the /m matches all files (the default) but by saying /m *.* you only match files with a dot!
My corrected version is:
::Batch
SET CDID=%~dp0
SET TEST=TRUE
IF %TEST%==TRUE (
SET COMND1=ECHO
SET COMND2=ECHO
) ELSE (
SET COMND1=DEL
SET COMND2=RD
)
ECHO FILE RAN %date:~10%/%date:~4,2%/%date:~7,2% >>%CDID%\LOG.TXT
FOR /F "usebackq delims=| tokens=1,2" %%x IN (%CDID%PATH.txt) DO (
CALL :DEL_FOLDERS "%%x" %%y
CALL :DEL_FILES "%%x" %%y
)
GOTO :EOF
:DEL_FILES
FORFILES /p %1 /s /d %2 /c "cmd /c %COMND1% #file" 2> nul
GOTO :EOF
:DEL_FOLDERS
FOR /f "delims=" %%i in ('dir "%~1" /s /b /ad 2^>nul ^| sort /r') do %COMND2% "%%i"
GOTO :EOF
::PATH.txt
C:\Temp\BLANK|10
C:\Temp\New folder|30

Related

How to move only the first file from a folder if this folder contains more than 100 files?

I'm trying to build a script that moves the first file (ordered by name) if the source folder contains more than 100 files in it.
I got here but it moves all the files instead of only one.
Can someone help me please?
for /f %%a in ('dir /b /a-d C:\SOURCE ^|find /c /v ""') do set "remaining=%%a"
forfiles /P C:\SOURCE /M * /C "cmd /c if #isdir==FALSE if %remaining% geq 100 move #FILE "C:\TARGET""
you don't need forfiles (by the way: you explicitly tell it to move all files (/m *):
#echo off
setlocal
cd /d "C:\SOURCE"
set num=0
for /f "tokens=1,* delims=:" %%a in ('dir /b /a-d /o-n * ^|findstr /n "^"') do set /a "num=%%a" & set "file=%%b"
echo debug: %num% files. To move: %file%
if %num% gtr 100 move "%file%" "c:\target\"

Return forfiles result if it finds a duplicate

I don't know how should I title my problem but here's what I'm trying to do:
I want to search for duplicates in the directory (which is from the input)
Then get the number of duplicates files
Here's where I'm stuck, if there's is no duplicate files, it should echo out "No duplicate files found", my idea is using >nul|set fileError="No duplicate files found"
I'm confused where should i placed it because I'm looping through the results.
To be direct, if there is no duplicate files [filenames] then it should echo "No duplicate files found".
Here's what I did:
set findFiles=0
for /f %%A in ('FORFILES /P "%filePath%" /S /M "%fileName%" /C "cmd /c echo #path"') do (
set /a findFiles+=1
echo %findFiles% - %%~A
)
Update: As to clarify what I wanted, My batch file should return the list of duplicate files if the result is greater than 1, if the return list is only one then it should echo out "No duplicate files found".
Based on my comments here are some examples for you.
First using your ForFiles method and delayed expansion:
For /F "Delims==" %%A In ('"(Set file[) 2>Nul"') Do Set "%%A="
Set "i=0"
SetLocal EnableDelayedExpansion
For /F "Delims=" %%A In (
'ForFiles /P "%filePath%" /S /M "%fileName%" /C "Cmd /C Echo #Path" 2^>Nul'
) Do Set /A i+=1 & Set "file[!i!]=%%~A"
If %i% Equ 0 Echo File %fileName% not found in %filePath% & GoTo End
If %i% Equ 1 Echo No duplicates of %fileName% found in %filePath% & GoTo End
For /L %%A In (1 1 %i%) Do Echo [%%A]!file[%%A]!
:End
Pause
Using For /F with Dir and delayed expansion:
For /F "Delims==" %%A In ('"(Set file[) 2>Nul"') Do Set "%%A="
Set "i=0"
SetLocal EnableDelayedExpansion
For /F "Delims=" %%A In ('Dir /B /S /A-D "%filePath%\%fileName%" 2^>Nul'
) Do Set /A i+=1 & Set "file[!i!]=%%A"
If %i% Equ 0 Echo File %fileName% not found in %filePath% & GoTo End
If %i% Equ 1 Echo No duplicates of %fileName% found in %filePath% & GoTo End
For /L %%A In (1 1 %i%) Do Echo [%%A]!file[%%A]!
:End
Pause
You could also use Where instead of Dir changing its line 4to:
For /F "Delims=" %%A In ('"Where /R "%filePath%" "%fileName%" 2>Nul"'
You could use RoboCopy changing line 4 to the following two lines:
Set "RCO=/S /L /XJ /R:0 /FP /NS /NC /NP /NDL /NJH /NJS"
For /F "Tokens=*" %%A In ('RoboCopy "%filePath%" Null "%fileName%" %RCO%'
Or possibly a For /R option changing line 4 to:
For /R "%filePath%" %%A In ("%fileName%?"
The For /R version is a little strange because it has to use a wildcard, whilst this could be problematic, it may be useful however should you doubt whether your file has the .doc or .docx extension; (it should match both)

How to add recursive directory to batch file

I have the following batch file:
#echo off
for /f "delims=" %%F in (
'dir /b /a-d [*]*'
) do for /f "tokens=1* delims=]" %%A in (
"%%F"
) do for /f "tokens=*" %%C in ("%%B") do ren "%%F" "%%C"
I want launch it in the root directory and have it go through all directories and subdirectories performing the actions.
I tried adding /D and /r to the 'for' lines, but it doesn't appear to be working.
Do I need add something like...
for /D /r do
under the #echo off ?
Use either dir or for to get all the files, don't mix it up.
When using dir /S for recursive enumeration, regard that full paths are output rather than pure file names only.
This should do it:
#echo off
for /f "delims=" %%F in (
'dir /s /b /a-d [*]*'
) do for /f "tokens=2* delims=]" %%B in (
"%%~nxF"
) do for /f "tokens=*" %%C in ("%%B") do ren "%%~F" "%%C"
So I just changed the following in your original code:
added /s to dir (returns full paths then);
improved second for options (you never used the first token %%A, so why extract it then?);
replaced set %%F of second for by %%~nxF to just parse the file name (type for /? for details concerning substitution modifiers such as ~n, ~x);
replaced source argument "%%F" of ren command by "%%~F" to not fall into double-double-quote problems (the ~ modifier removes potential double-quotes);
You are using "dir" for the enumeration of files, so add "/s" to the DIR command.
I might refactor what you have like this to make it easier to manage.
This also does recursion.
call :TOP .
goto :EOF
:TOP
setlocal
cd "%~f1"
for /f "delims=" %%F in ('dir /b /a-d [*]*') do call :SubRoutine "%%F"
for /D %%x in (*) do call :TOP "%%x" || (echo FAILED2 "%%x" && exit /b 2)
goto :EOF
:SubRoutine
for /f "tokens=1* delims=]" %%A in ("%~1") do call :SubRoutine2 "%~1" "%%A" "%%B"
goto :EOF
:SubRoutine2
for /f "tokens=*" %%C in ("%~3") do ren "%~1" "%%C"
goto :EOF

batch file command to compare newest file and output differences

I have the below code which someone gave to me but I don't know how to put it together in a bat file so it runs successfully.
The aim is to find the latest (last modified) file in c:/ and compare it with c:/2.txt and output the differences into c:/786.txt
cd /d c:\
for /f %%a in ('dir /b /o-d /a-d /tw') do (set latest=%%a & goto :eof)
for /f "tokens=1*" %%a in (
'diff c:\%latest% c:\2.txt ^| findstr /r /c:"^<" /c:"^>"'
) do #echo %%b >>c:\786.txt
Can someone please put this code together for me.
cd /d c:\
set "latest="
for /f %%a in ('dir /b /o-d /a-d /tw') do (set "latest=%%a" & goto :found)
:found
if not defined latest exit /b
for /f "tokens=1,*" %%a in (
'diff "c:\%latest%" "c:\2.txt" ^| findstr /r /c:"^<" /c:"^>"'
) do (
>> "c:\786.txt" echo(%%b
)
Ordering by date descending, the latest file is the first, so on first iteration assign the file name and exit of the for loop.
Then check if any file has been found. It not, end of the script
If we have a file, compare latest file against the indicated one and send the filtered lines to the final file.
EDIT - Refactor code to made it more usable and adapt to comments. Search for last file in folder moved to a subroutine.
#echo off
setlocal enableextensions disabledelayedexpansion
call :getLatestFileInFolder "c:\" latestC
call :getLatestFileInFolder "d:\" latestD
if not defined latestC ( echo NO File in C & exit /b )
if not defined latestD ( echo NO File in D & exit /b )
for /f "tokens=1,*" %%a in (
'diff "%latestC%" "%latestD%" ^| findstr /r /c:"^<" /c:"^>"'
) do (
>> "c:\786.txt" echo(%%b
)
endlocal
exit /b
:getLatestFileInFolder folderToSearch variableToReturn
setlocal
set "folder=%~1" & if not defined folder set "folder=%cd%"
set "latest="
pushd "%folder%"
for /f "tokens=*" %%a in ('dir /b /o-d /a-d /tw 2^>nul') do (set "latest=%%~fa" & goto :latestFileFound)
:latestFileFound
popd
endlocal & set "%~2=%latest%" & goto :eof

Batch File - Recursively check folders and move files

I am trying to create a windows batch file that will scan a folder with many sub folders. Each sub folder can contain many files. I need the script to check if a sub folder contains over a certain number of files, and if it does move half of the files to a new folder with the same name but with a number at the end.
Example:
Main folder
-Subfolderone
-Subfoldertwo
-Subfolderthree
If Subfoldertwo contains over a certain number of files, lets say 1000, then half of the files within Subfoldertwo will be moved to Subfoldertwo(2), and so on for each sub folder.
Main folder
-Subfolderone
-Subfoldertwo
-Subfoldertwo(2)
-Subfolderthree
Any help would be much appreciated. Thank you.
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
SET limit=5
FOR /f "delims=" %%a IN ('dir /b /s /ad "%sourcedir%\*"') DO (
SET /a newnum=2
FOR /f %%c IN ('dir /b/a-d "%%~a" 2^>nul ^|find /c /v ""') DO IF %%c gtr %limit% CALL :process "%%a"
)
)
GOTO :EOF
:process
IF EXIST "%~1(%newnum%)\" SET /a newnum+=1&GOTO process
ECHO MD "%~1(%newnum%)"
FOR /f "skip=%limit%delims=" %%m IN ('dir /b /a-d "%~1"') DO ECHO MOVE "%~1\%%m" "%~1(%newnum%)\"
GOTO :eof
Simple enough. I've set the sourcedir to a constant for my testing and the limit to 5 for the same reason.
First build a list of the original diretory tree, then count the files in each directory. If that count is greater than the limit, process the directory.
In process, first find whether the proposed new directory already exists. If it does, keep incrementing the number 'til it doesn't.
Then list the filenames (only) from the original full-directoryname, skipping the first %limit% and for the remainder, move them to the new directoryname.
The required commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
AND change ECHO MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Edit : revised for 'move half the files'
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
SET limit=5
FOR /f "delims=" %%a IN ('dir /b /s /ad "%sourcedir%\*"') DO (
SET /a newnum=2
FOR /f %%c IN ('dir /b/a-d "%%~a" 2^>nul ^|find /c /v ""') DO IF %%c gtr %limit% SET /a nmove=%%c / 2&CALL :process "%%a"
)
)
GOTO :EOF
:process
IF EXIST "%~1(%newnum%)\" SET /a newnum+=1&GOTO process
ECHO MD "%~1(%newnum%)"
FOR /f "skip=%nmove%delims=" %%m IN ('dir /b /a-d "%~1"') DO ECHO MOVE "%~1\%%m" "%~1(%newnum%)\"
GOTO :eof
(simply calculate half of the count into nmove then skip that number instead)
you might test this:
#ECHO OFF &SETLOCAL
set "StartFolder=X:\Main folder"
set /a MaxFiles=1000
cd /d "%StartFolder%"
:NewFolderCreated
set "NewFolderFlag="
for /f "delims=" %%a in ('dir /b /ad /on') do call:process "%StartFolder%\%%~a"
if defined NewFolderFlag (goto:NewFolderCreated) else goto:eof
:process
SETLOCAL
cd "%~1"
for /f %%b in ('dir /b /a-d 2^>nul^|find /c /v ""') do set /a FileCount=%%b
if %FileCount% leq %MaxFiles% exit /b
set /a MoveCount=FileCount-MaxFiles
set "CurrentFolder=%~n1"
set "NextPath=%StartFolder%\%CurrentFolder%(2)%~X1"
echo("%CurrentFolder%"|findstr /re ".*([0-9][0-9]*)\"^">nul||goto:moving
set "BasePath=%CurrentFolder:~0,-1%"
:loop
if not "%BasePath:~-1%"=="(" set "FolderNo=%BasePath:~-1%%FolderNo%"&set "BasePath=%BasePath:~0,-1%"&goto:loop
set /a FolderNo+=1
set "NextPath=%StartFolder%\%BasePath%%FolderNo%)%~X1"
:moving
echo(Moving %MoveCount% files from "%~1" to "%NextPath%".
md "%NextPath%" 2>nul &&set "NewFolderFlag=true"
for /f "skip=%MaxFiles%delims=" %%b in ('dir /b /a-d /o-n') do move "%~1\%%~b" "%NextPath%" >nul
endlocal &set "NewFolderFlag=%NewFolderFlag%"
exit /b

Resources