For /f only outputting top level from forfiles /s - windows

I have the following directory structure structure:
DirA
DirB
fileb1
fileb2
DirC
filec1
filec2
fileA1
my.appcache
fileA2
And I'm trying to write a batch file that will output the file names with their relative directories to a .appcache or .txt file.
Here is the script which lies in the directory above DirA
cd DirA
del /Q "my.appcache"
fsutil file createnew my.appcache 0
setlocal disableDelayedExpansion
for /f "delims=" %%A in ('forfiles /s /m *.* /c "cmd /c echo #relpath >> my.appcache" ') do (
set "file=%%~A"
setlocal enableDelayedExpansion
echo !file:~2!
endlocal
)
PAUSE
However, the script only sends DirA's contents to my.appcache. However, if i just let it echo to the cmd window, it lists all the files inside DirB and DirC too.
Can anyone see what I'm doing wrong?
EDIT: It's creating my.appcaches in DIRB and DIRC and outputting the relevant section to them.

Your script is a bit hard to follow because you are combining for /f with forfiles. Those two commands do a similar job so people typically use one or the other. Anyway, your issue is that you are appending to a file in the current directory, which changes as the forfiles command traverses the tree. You'll find a separate my.appcache file in each directory. You should instead do the redirection in one place:
(forfiles /s /m *.* /c "cmd /c echo #relpath") > my.appcache
(and you don't need any of the other scaffolding.)
By the way, if you want to search for ALL files (not just those containing a dot in their names) then the /m * option is the default, so you can just leave it out:
(forfiles /s /c "cmd /c echo #relpath") > my.appcache

Related

Windows Batch:Count the number of files deleted using forfiles command

I have written a simple batch job for copying the files from one folder to another that are older than x-number of days using forfiles. But I need to count the number of files that have been copied. I tried various ways to do this but was unsuccessful. Could any body help me trough this?
#Echo off
Echo Starting the script for copying files to other folder.
set dt=%date:~10,4%-%date:~4,2%-%date:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
set filesMovedCount=0
forfiles /p C:\symphonybackup\symphonybackup\Symphony\SymphonyArcheive /s /m *.* /d -30 /c "cmd /c echo #path >> C:\symphonybackup\symphonybackup\Symphony\%dt%.log & cmd /c copy #path C:\symphonybackup\symphonybackup\Symphony\SymphonyTemp"
Echo filesmovedcount:%filesMovedCount%
Echo finised copying the files
You could just pipe all of your copied file names to a file and then count the rows in the file. Here I added the additional stuff, you can just add the commands where the output gets piped to logfile where indicated. When done, it will echo the count and you delete the file.
#echo off
forfiles /p "rest of copy commands" >>logfile.txt
setlocal EnableDelayedExpansion
set "string=findstr /R /N "^^" logfile.txt | find /C ":""
for /f %%a in ('!string!') do set count=%%a
echo %count% files copied
del /Q logfile.txt
Mayby late answer to this, but when I try this:
set "string=findstr /R /N "^^" logfile.txt | find /C ":""
for /f %%a in ('!string!') do set count=%%a
I get one extra result since I get one empty row. So I did need have to add -1 to the result.
Atleast on my system...

How to delete *.bak files recursively older than a specific date depending on directory in file path?

I'm writing a batch file for Windows 7.
I currently have a code that deletes old backups from our masters folders within our site management folders. This is the code:
for /d %%A in ("Y:\*.*") do del /s /q /f "%%A\masters\*.bak"
However I need to code it to only delete things that are older than 3 years, which would be this code:
forfiles /P "Y:\" /S /D -1096 /M *.bak /C "cmd /C del #path"
However I need what is in the top code so that I can delete all *.bak files from the masters folders that exist within our 173 site management folders. I'm ripping my hair out figuring this out. I can't have it deleting *.bak files from our other folders.
I've tried combining the code, but below command line in batch file does not work as expected:
forfiles /S /D -1096 /M *.bak /C "cmd /C for /d %%A in ("Y:\*.*") do del /s /q /f "%%A\masters\*.bak"
How to delete all *.bak files older than 3 years anywhere in directory tree if second directory in file path is masters and keep all other *.bak files being newer or in a directory where second directory in file path is not masters?
Create first a batch file C:\Temp\DeleteBackup.bat with the following commands:
#echo off
set "BackupFileName=%~1"
if not "%BackupFileName:\masters\=%" == "%BackupFileName%" ECHO del "%BackupFileName%"
This batch code checks if the file name with full path and file extension contains anywhere \masters\ by removing this string case-insensitive from left argument of string comparison.
If the remaining string is not equal the unmodified file name string because of containing \masters\ in path, the IF condition is true and the backup file would be deleted if there would not be command ECHO which results in just displaying the DEL command line.
For example the complete list of backup files is:
Y:\masters\Level2\Level3\Level4\Level5\Test1.bak
Y:\Folder2\masters\Level3\Test2.bak
Y:\Folder3\Level2\masters\Level4\Test3.bak
Y:\Folder4\Level2\Level3\Level4\Level5\Test4.bak
Y:\Folder5\Level2\Test5.bak
Y:\Folder6\Level2\Level3\Level4\Level5\Level6\Test6.bak
Y:\Folder7\masters\Test7.bak
The files deleted would be:
Y:\masters\Level2\Level3\Level4\Level5\Test1.bak
Y:\Folder2\masters\Level3\Test2.bak
Y:\Folder3\Level2\masters\Level4\Test3.bak
Y:\Folder7\masters\Test7.bak
And the files remaining would be:
Y:\Folder4\Level2\Level3\Level4\Level5\Test4.bak
Y:\Folder5\Level2\Test5.bak
Y:\Folder6\Level2\Level3\Level4\Level5\Level6\Test6.bak
Then use in your batch file:
forfiles /P "Y:\" /S /D -1096 /M *.bak /C "C:\Temp\DeleteBackup.bat #PATH"
It is of course possible to modify DeleteBackup.bat to check if directory in second directory hierarchy level is masters.
#echo off
for /F "tokens=3 delims=\" %%I in ("%~1") do if /I "%%I" == "masters" ECHO del "%~1"
This code would delete from the complete list above the files:
Y:\Folder2\masters\Level3\Test2.bak
Y:\Folder7\masters\Test7.bak
And the files remaining would be:
Y:\masters\Level2\Level3\Level4\Level5\Test1.bak
Y:\Folder3\Level2\masters\Level4\Test3.bak
Y:\Folder4\Level2\Level3\Level4\Level5\Test4.bak
Y:\Folder5\Level2\Test5.bak
Y:\Folder6\Level2\Level3\Level4\Level5\Level6\Test6.bak
Robert Chizmadia Jr. asked in an already deleted comment:
Is it possible to use GOTO instead of calling another batch file on FORFILES command line?
The answer on this additional question:
FORFILES is not an internal command of cmd.exe like FOR. It is a console application stored in directory %SystemRoot%\System32 if used version of Windows has it pre-installed at all.
The command to execute as specified after FORFILES option /C must be an executable or script. That is the reason why cmd /C is always used when an internal command of Windows command interpreter cmd.exe like DEL should be executed by FORFILES whereby the really complete command would be %SystemRoot%\System32\cmd.exe /C.
So it is not possible to use a command like GOTO in FORFILES command as there is no executable or script with name GOTO.
Also GOTO in a FOR loop exits the loop and therefore interpreting of command lines of batch files continues on another position in batch file.
However, it is possible to use the same batch file for the file path evaluation and backup file deletion as used to run FORFILES command.
Example 1 with batch file not expecting any parameter for default operation:
#echo off
if not "%~1" == "" (
for /F "tokens=3 delims=\" %%I in ("%~1") do if /I "%%I" == "masters" ECHO del "%~1"
goto :EOF
)
%SystemRoot%\System32\forfiles.exe /P "Y:\" /S /D -1096 /M *.bak /C "%~f0 #PATH"
If this batch file is executed with an argument, it runs the FOR loop written to check if second directory in file path is masters and delete this file in this case after removing ECHO. Otherwise on starting the batch file without any parameter the batch file runs the FORFILES executable.
Example 2 with batch file expecting 1 or more parameters for default operation:
#echo off
if "%~1" == "#Delete:Backup#" (
for /F "tokens=3 delims=\" %%I in ("%~2") do if /I "%%I" == "masters" ECHO del "%~2"
goto :EOF
)
rem Other commands processing the parameters.
%SystemRoot%\System32\forfiles.exe /P "Y:\" /S /D -1096 /M *.bak /C "%~f0 #Delete:Backup# #PATH"
rem More commands executed after the deletion of the backup files.
This is nearly the same as example 1 with the difference that if first parameter used on running the batch file is case-sensitive the string #Delete:Backup#, the batch file expects as second parameter the name of a backup file with full path being deleted if second directory in file path is masters.
Like in all batch code examples the command ECHO must be removed before del command also in this code example to really execute the deletion of the backup files.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?
forfiles /?
goto /?
if /?
set /?

FORFILES no longer recursive when used with FOR loop

I have this code below in a batch file (Windows). It returns a list of paths for each .jpg file in a directory. Then passes that list to the ImageMagick Convert function (not the Windows convert) to create a pdf.
echo moving CMD to drive location at %~dp1
CD /d %~dp1
echo getting the list of file names
FORFILES /p %~dp1 /s /m "*.jpg" /C "cmd /c echo #path" > files.txt
echo creating the pdf using ImageMagick
convert #files.txt test.pdf
This works fine for returning the jpg image data or another single file type. I need it to search for multiple image file types and I have seen this example solution where you can put the FORFILES into a FOR loop.
for %%G in ( .jpg , .tif ) do FORFILES /p %~dp1 /s /m "*.jpg" /C "cmd /c echo #path" > files.txt
But if I do that the program is no longer recursive. The /s in forfiles no longer works.
I have tried FOR /R but it doesn't handle folder names with spaces which I need to be able to do.
Any thoughts on how to keep this recursive and not have issues with folder names with spaces?
#Lưu Vĩnh Phúc
thank you for responding. I tried what you said but For /F was throwing an error where it thought .jpg was a file and it couldnt find it.
You did force me to try new things and in doing so I think it was just my syntax that was the issue. I changed the code to this:
FOR %%G in (.jpg, .tif) do FORFILES /p %~dp1 /s /m *%%G /C "cmd /c echo #path" >> files.txt
Where I removed the /r or /f and added the variable from the for loop to the forfiles loop.
This keeps its recursive nature and returns only the file types listed in the for loop.

Delete all files in directory except in one subdirectory

I have this script:
forfiles /p "C:\Hello" /m *.* /s /d -5 /c "cmd /c if /i not #path==C:\Hello\Dontdelete del #file"
I'm trying to delete every file older than 5 days in C:\Hello except for files that are in C:\Hello\Dontdelete or any directory within this path.
Currently everything is getting deleted when I use the above script.
Thanks!
The fundamental problem is that #path is the full pathname - including the filename+extension, and quoted and #file is the filename+extension, also quoted. In consequence, the if statement as constructed can't be manipulated to work as anticipated.
Here's a way to get it working (I've made a few changes to suit my system which should be easy to manipulate to suit yours)
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
forfiles /p "%sourcedir%" /m *.* /s /d -6 /c "cmd /c call %cd%\selectdel #PATH"
GOTO :EOF
I set up selectdel as a batch in my current directory. No doubt it would be fine on the path, or simply forced as I have with the %cd%\.
selectdel.bat
#echo off
setlocal
if /i not "%~dp1"=="%sourcedir%\Dontdelete\" echo DEL %1
GOTO :eof
Obviously, I've simply echoed the del command - in case of accidents...
I do not know much about it, but I think you should make a lopping for every 5 days, the script did a check to see if there are files in the folder C :/ Hello, if yes, the script clears, if not, it does nothing. Search about it.

How to delete empty folders using windows command prompt?

I need to delete all empty folders from my application folder using windows command prompt?
How can I create a bat file like that?
Please help me.
You can use the ROBOCOPY command. It is very simple and can also be used to delete empty folders inside large hierarchy.
ROBOCOPY folder1 folder1 /S /MOVE
Here both source and destination are folder1, as you only need to delete empty folders, instead of moving other(required) files to different folder. /S option is to skip copying(moving - in the above case) empty folders. It is also faster as the files are moved inside the same drive.
A simpler way is to do xcopy to make a copy of the entire directory structure using /s switch. help for /s says Copies directories and subdirectories except empty ones.
xcopy dirA dirB /S
where dirA is source with Empty folders. DirB will be the copy without empty folders
for /f "usebackq" %%d in (`"dir /ad/b/s | sort /R"`) do rd "%%d"
from: http://blogs.msdn.com/b/oldnewthing/archive/2008/04/17/8399914.aspx
Of course I'd test it first without deleting before I do that command. Also, here's a modded version from the comments that includes folders with spaces:
for /f "usebackq delims=" %%d in (`"dir /ad/b/s | sort /R"`) do rd "%%d"
P.S. there are more comments in the blog post that might help you out so be sure to read those too before you try this out
You don't need usebackq:
FOR /F delims^= %%A IN ('DIR/AD/B/S^|SORT/R') DO RD "%%A"
Adding to corroded answer from the same referenced page is a PowerShell version http://blogs.msdn.com/b/oldnewthing/archive/2008/04/17/8399914.aspx#8408736
Get-ChildItem -Recurse . | where { $_.PSISContainer -and #( $_ | Get-ChildItem ).Count -eq 0 } | Remove-Item
or, more tersely,
gci -R . | where { $_.PSISContainer -and #( $_ | gci ).Count -eq 0 } | ri
credit goes to the posting author
from the command line:
for /R /D %1 in (*) do rd "%1"
in a batch file
for /R /D %%1 in (*) do rd "%%1"
I don't know if it's documented as such, but it works in W2K, XP, and Win 7. And I don't know if it will always work, but it won't ever delete files by accident.
This is a hybird of the above. It removes ALL files older than X days and removes any empty folders for the given path. To use simply set the days, folderpath and drive
#echo off
SETLOCAL
set days=30
set folderpath=E:\TEST\
set drive=E:
::Delete files
forfiles -p %folderpath% -s -d -%days% -c "cmd /c del /q #path "
::Delete folders
cd %folderpath%
%drive%
for /f "usebackq delims=" %%d in (`"dir /ad/b/s | sort /R"`) do rd "%%d"`
It will be worked fine. This is best way to delete old files and remove empty directories recursively.
following .bat file is,
forfiles /p [PATH] /s /m [FILE-PATTERN] /d -[DAYS] /c "cmd /c del #path"
for /f "delims=" %%d in ('dir [PATH] /s /b /ad ^| sort /r') do rd "%%d"
The placeholders needs to be replaced as follows (without the quotation marks):
[DAYS] = Max. age of the files in days, e.g. “10”
[PATH] = Path to search for old files and empty folders, e.g. “C:\Backup\”
[FILE-PATTERN] = Pattern that matches files to delete, e.g. “*.bkp”
The script has been successfully tested under Windows 7 and Windows Server 2003.
Install any UNIX interpreter for windows (Cygwin or Git Bash) and run the cmd:
find /path/to/directory -empty -type d
To find them
find /path/to/directory -empty -type d -delete
To delete them
(not really using the windows cmd prompt but it's easy and took few seconds to run)
#echo off
set /p "ipa= ENTER FOLDER NAME TO DELETE> "
set ipad="%ipa%"
IF not EXIST %ipad% GOTO notfound
IF EXIST %ipad% GOTO found
:found
echo DONOT CLOSE THIS WINDOW
md ccooppyy
xcopy %ipad%\*.* ccooppyy /s > NUL
rd %ipad% /s /q
ren ccooppyy %ipad%
cls
echo SUCCESS, PRESS ANY KEY TO EXIT
pause > NUL
exit
:notfound
echo I COULDN'T FIND THE FOLDER %ipad%
pause
exit
If you want to use Varun's ROBOCOPY command line in the Explorer context menu (i.e. right-click) here is a Windows registry import. I tried adding this as a comment to his answer, but the inline markup wasn't feasible.
I've tested this on my own Windows 10 PC, but use at your own risk. It will open a new command prompt, run the command, and pause so you can see the output.
Copy into a new text file:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\directory\Background\shell\Delete Empty Folders\command]
#="C:\Windows\System32\Cmd.exe /C \"C:\Windows\System32\Robocopy.exe \"%V\" \"%V\" /s /move\" && PAUSE"
[HKEY_CURRENT_USER\Software\Classes\directory\shell\Delete Empty Folders\command]
#="C:\Windows\System32\Cmd.exe /C \"C:\Windows\System32\Robocopy.exe \"%V\" \"%V\" /s /move\" && PAUSE"
Rename the .txt extension to .reg
Double click to import.
for /r "D:\Music" /d %F in (.) do #dir /b "%F" | findstr "^" >nul || rmdir %~fF
D:\Fun is the folder that contains empty folders
double quotation in the case of space in folder name (no need in this example)
A more modern and easier solution is:
forfiles /p C:\Path\To\Folder /c "cmd /c if #isdir==TRUE rd #file"
This will attempt to execute rd on all the directories located in C:\Path\To\Folder. Directories that have content in them will not be deleted.
If you exclude /p C:\Path\To\Folder then it'll run in the current directory.
If you add /s (before the /c) then it'll also look in sub-directories.
The following .cmd is an experiment (that works) to:
Deletes empty directories and included "Old"(-1days) files under %temp% & C:\Windows\Temp folders,
makes an cmd output log to a .txt file, about fouded & deleted folders/files .
%temp% = C:\Users(user)\AppData\Local\Temp | %userprofile%\AppData\Local\Temp
code:
:: This is a .cmd file
:: Use to:
:: Writes a Log about Temporary files & folders.
:: Cleans 'Windows Temp' folders/files. (%userprofile%\AppData\Local\Temp -&- %windir%\Temp)
:: - a 'Cleaning_LOGs.txt' will be created or updated in Documents Library. (%userprofile%\Documents\Cleaning_LOGs.txt)
::
#echo off
title Log&CleanTemp
: Set the Path of 'Log file' (%LogPath% variable)
set "LogPath=%userprofile%\Documents\Cleaning_LOGs.txt"
:: Note: ">> path\file.txt 2>&1" redirects cmd output to <path\to>\<log_file.txt>, (will be created if does not exist)
:: (if exist, adds the new log at the end of the file, without deleting previous logs)
: Set 'C:\Windows\Temp' (%WinTemp% var.)
set "WinTemp=%windir%\Temp"
: Seperator [Header] with Date-Time between (any) previous Logs in <log_file.txt>
echo: >> %LogPath% 2>&1
echo ======================================== >> %LogPath% 2>&1
echo %date% - %time% >> %LogPath% 2>&1
echo: >> %LogPath% 2>&1
echo Log Path: %LogPath% (this text file) >> %LogPath% 2>&1
echo: >> %LogPath% 2>&1
: Report Output & Log
:: Writes a log about temporary files & folders.
:: Note: ( %temp% = C:\Users\<user>\AppData\Local\Temp = %userprofile%\AppData\Local\Temp )
:: ( 'WinTemp' = C:\Windows\Temp = %windir%\Temp )
echo: >> %LogPath% 2>&1
echo __________ Empty (0 size) , Old (-1days) files: >> %LogPath% 2>&1
ForFiles /p "%temp%" /s /d -1 /c "cmd /c if #fsize==0 ECHO #path " >> "%LogPath%" 2>&1
ForFiles /p "%WinTemp%" /s /d -1 /c "cmd /c if #fsize==0 ECHO #path " >> "%LogPath%" 2>&1
echo: >> %LogPath% 2>&1
echo __________ All Old (-1days) files: >> %LogPath% 2>&1
ForFiles /p "%temp%" /s /d -1 /c "cmd /c ECHO #path " >> "%LogPath%" 2>&1
ForFiles /p "%WinTemp%" /s /d -1 /c "cmd /c ECHO #path " >> "%LogPath%" 2>&1
:: Note: "ForFiles" /p=Path /s=SubDir /d=Days(dd) /c=cmd "forfiles /?" for info about command's Variables (#path, #file, etc.)
: Get permissions (unlock files/folders) (OPTIONAL)
:: Uncomment to make it work, IF needed.
::echo: >> %LogPath% 2>&1
::ForFiles /p "%temp%" /s /d -1 /c "cmd /c TAKEOWN /f * /r /d y && ICACLS #file /grant *S-1-3-4:F /t /c /l /q"
: Clean proper files & Log it
:: Test: ForFiles /p "%temp%\" /s /d -1 /c "cmd /c if #fsize==0 DEL /f /s /q #file" >> "%LogPath%" 2>&1
:: ERROR: Invalid argument/option - '#fsize==0'
echo: >> %LogPath% 2>&1
echo __________ Deleted files: >> %LogPath% 2>&1
forfiles /p %temp%\ /s /d -1 /c "cmd /c DEL /f /s /q #path" >> "%LogPath%" 2>&1
forfiles /p %WinTemp%\ /s /d -1 /c "cmd /c DEL /f /s /q #path" >> "%LogPath%" 2>&1
echo: >> %LogPath% 2>&1
echo __________ Deleted empty directories: >> %LogPath% 2>&1
for /f "delims=" %%d in ('dir %temp%\ /s /b /ad ^| sort /r') do RD "%%d" >> "%LogPath%" 2>&1
for /f "delims=" %%d in ('dir %WinTemp%\ /s /b /ad ^| sort /r') do RD "%%d" >> "%LogPath%" 2>&1
:: Note: 'RD' = Remove-Directory (delete)
: Open Log file
:: this opens the log file [(my) Documents\Cleaning_LOGs.txt]
explorer.exe %LogPath%
:: https://stackoverflow.com/questions/7831286/how-to-delete-empty-folders-using-windows-command-prompt/46617314#46617314
Note that:
The trigger to Experiment with this, as part of a 'pre-process', was to prevent the specific (obsolete) driver of the (also obsolete) sound card from automatically reinstalling at each boot. And clean (ghosted) hidden Devices and more other. (...) (this note was just about to get an idea for the use)
So, in a few words:
This is the part that cleans up the 'temp' garbage left behind and gives a report.
well, just a quick and dirty suggestion for simple 1-level directory structure without spaces, [edit] and for directories containing only ONE type of files that I found useful (at some point from http://www.pcreview.co.uk/forums/can-check-if-folder-empty-bat-file-t1468868.html):
for /f %a in ('dir /ad/b') do if not exist %a\*.xml echo %a Empty
/ad : shows only directory entries
/b : use bare format (just names)
[edit] using plain asterisk to check for ANY file (%a\* above) won't work, thanks for correction
therefore, deleting would be:
for /f %a in ('dir /ad/b') do if not exist %a\*.xml rmdir %a
This can be easily done by using rd command with two parameters:
rd <folder> /Q /S
/Q - Quiet mode, do not ask if ok to remove a directory tree with
/S
/S - Removes all directories and files in the specified directory
in addition to the directory itself. Used to remove a directory tree.

Resources