Search code examples
batch-filecmd

How delete all files and folders in a directory respectively an entire directory tree except files specified in a list file?


I am wondering how I can solve the following with a batch file.

Delete all files and folders inside G:\AD\mods except for a list of all files and folders I want to keep.

Has anyone some suggestions for a batch file solution?

I have tried the following Windows command line in a batch file:

for %%i in (*.*) do if not "%%i"=="doc1.txt" if not "%%i"=="doc2.txt" del /q "%%i"

This works, but I was only able to delete files from the same folder as the batch file was placed in.

Then I read this post: Delete all files in folder except file in list using batch

It suggests something like:

except 3.txt del temp /Q

This also works in the same folder, but not for files and folders in different levels of the directory tree.

EDIT:

I have a main folder with about 1000 sub folders. These folders also have sub folders in them. The same file name might be present in multiple folders. So I need to specify the exact file path for each file I want to keep.

Example for the names in the list file of the files to keep:

AD\mods\folder1\doc1.txt
AD\mods\folder1\subfolder1\doc1.txt
AD\mods\folder2\doc1.txt

All other files should be removed by the batch file.

A PowerShell solution would be also okay.


Solution

  • Let us assume the list file G:\AD\mods\Exclusion List.txt contains only file/folder names without path like:

    Cleanup.cmd
    Exclusion List.txt
    Folder to keep
    File not to delete.txt
    

    The batch file G:\AD\mods\Cleanup.cmd can be used with the following command lines to delete all files and folders in directory of the batch file with exception of the files and folders in exclusion list file:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    set "FolderPath=%~dp0"
    for /F "eol=| delims=" %%I in ('dir "%FolderPath%" /AD /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /X /G:"%FolderPath%Exclusion List.txt"') do rd /Q /S "%FolderPath%%%I"
    for /F "eol=| delims=" %%I in ('dir "%FolderPath%" /A-D /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /X /G:"%FolderPath%Exclusion List.txt"') do del /A /F "%FolderPath%%%I"
    endlocal
    

    The first FOR command line runs with using a separate command process started in background the command DIR to output all folder names in directory of the batch file filtered with FINDSTR to exclude from the folder names list all folder names listed in the exclusion list file. The resulting list is processed by FOR which runs command RD to delete these folders.

    The second FOR command line runs with using a separate command process started in background the command DIR to output all file names in directory of the batch file filtered with FINDSTR to exclude from the file names list all file names listed in the exclusion list file. The resulting list is processed by FOR which runs command DEL to delete these files.

    The FOR option eol=| is used to avoid ignoring file/folder names starting with a semicolon as no file/folder name can contain a vertical bar. The FOR option delims= defines an empty list of string delimiters resulting in turning off the default behavior of for /F to split the captured lines into substrings (tokens) using normal space and horizontal tab as string delimiters and assign only the first space/tab separated string to the specified loop variable I. That option is necessary to correct process also file/folder names with one or more spaces.


    The task requirements changed to delete all files in entire directory tree except the files listed in an exclusion list file with path (without drive letter and colon).

    So let us assume the file G:\AD\mods\Exclusion List.txt contains the following lines.

    G:\AD\mods\Cleanup.cmd
    G:\AD\mods\Exclusion List.txt
    AD\mods\folder1\doc1.txt
    AD\mods\folder1\subfolder1\doc1.txt
    AD\mods\folder2\doc1.txt
    

    The batch file G:\AD\mods\Cleanup.cmd can be used with the following command lines to delete first all files in entire directory tree of the directory containing the batch file and the exclusion list file with exception of the files in the exclusion list file and next delete all directories recursively which do not contain anymore at least one file.

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    set "FolderPath=%~dp0"
    if "%FolderPath:~-1%" == "\" set "FolderPath=%FolderPath:~0,-1%"
    for /F "delims=" %%I in ('dir "%FolderPath%" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /E /I /L /V /G:"%FolderPath%\Exclusion List.txt"') do del /A /F "%%I"
    call :DeleteEmptyFolders "%FolderPath%"
    goto EndBatch
    
    :DeleteEmptyFolders
    for /F "eol=| delims=" %%I in ('dir %1 /AD /B 2^>nul') do call :DeleteEmptyFolders "%~1\%%I"
    for /F "eol=| delims=" %%I in ('dir %1 /A /B 2^>nul') do goto :EOF
    rd /Q /S %1
    goto :EOF
    
    :EndBatch
    endlocal
    

    Please note that the FINDSTR option /X is replaced in first FOR command line by the option /E to filter out all file names of which full qualified name just ends with one of the strings in the exclusion list file instead of matching the entire file name because of the list file contains the file names without drive letter and colon. That could result in a wrong filtering depending on which part of the full path of a file is in the list file.

    So what happens with this batch file on following directory tree on drive G::

    • AD
      • backups
        • folder1
          • subfolder1
            • ;File to keep.txt
            • doc1.txt
          • doc1.txt
      • mods
        • folder1
          • subfolder1
            • ;File to delete.txt
            • doc1.txt
          • doc1.txt
          • One More File!.txt
        • folder2
          • Empty subfolder 1
          • Subfolder 2
            • Another Empty Folder
            • doc1.txt
          • doc1.txt
          • Log file in folder2().log
        • folder3
          • doc1.txt
        • Cleanup.cmd
        • Exclusion List.txt
        • Readme.txt

    The result of the execution of G:\AD\mods\Cleanup.cmd is:

    • AD
      • backups
        • folder1
          • subfolder1
            • ;File to keep.txt
            • doc1.txt
          • doc1.txt
      • mods
        • folder1
          • subfolder1
            • doc1.txt
          • doc1.txt
        • folder2
          • doc1.txt
        • Cleanup.cmd
        • Exclusion List.txt

    So everything in G:\AD\backups is kept and in G:\AD\mods only the folders with files of which names are listed in the exclusion list file remain and all other files and folders are deleted by the batch script.

    The directories and files can be created with following batch code with exception of Cleanup.cmd and Exclusion List.txt in G:\AD\mods:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    set "BasePath=G:"
    md "%BasePath%\AD\backups\folder1\subfolder1" 2>nul
    md "%BasePath%\AD\mods\folder1\subfolder1" 2>nul
    md "%BasePath%\AD\mods\folder2\Empty subfolder 1" 2>nul
    md "%BasePath%\AD\mods\folder2\Subfolder 2\Another Empty Folder" 2>nul
    md "%BasePath%\AD\mods\folder3" 2>nul
    setlocal EnableDelayedExpansion
    echo !BasePath!\AD\backups\folder1\subfolder1\;File to keep.txt>"!BasePath!\AD\backups\folder1\subfolder1\;File to keep.txt"
    echo !BasePath!\AD\backups\folder1\subfolder1\doc1.txt>"!BasePath!\AD\backups\folder1\subfolder1\doc1.txt"
    echo !BasePath!\AD\backups\folder1\doc1.txt>"!BasePath!\AD\backups\folder1\doc1.txt"
    (echo !BasePath!\AD\mods\folder1\subfolder1\;File to delete.txt>"!BasePath!\AD\mods\folder1\subfolder1\;File to delete.txt") 2>nul
    echo !BasePath!\AD\mods\folder1\subfolder1\doc1.txt>"!BasePath!\AD\mods\folder1\subfolder1\doc1.txt"
    echo !BasePath!\AD\mods\folder1\doc1.txt>"!BasePath!\AD\mods\folder1\doc1.txt"
    echo !BasePath!\AD\mods\folder1\One More File^^!.txt>"!BasePath!\AD\mods\folder1\One More File^!.txt"
    (echo !BasePath!\AD\mods\folder2\Subfolder 2\doc1.txt>"!BasePath!\AD\mods\folder2\Subfolder 2\doc1.txt") 2>nul
    echo !BasePath!\AD\mods\folder2\doc1.txt>"!BasePath!\AD\mods\folder2\doc1.txt"
    echo !BasePath!\AD\mods\folder2\Log file in folder2().log>"!BasePath!\AD\mods\folder2\Log file in folder2().log"
    echo !BasePath!\AD\mods\folder3\doc1.txt>"!BasePath!\AD\mods\folder3\doc1.txt"
    echo !BasePath!\AD\mods\Readme.txt>"!BasePath!\AD\mods\Readme.txt"
    endlocal
    %SystemRoot%\System32\attrib.exe +r "%BasePath%\AD\mods\folder1\subfolder1\;File to delete.txt"
    %SystemRoot%\System32\attrib.exe +h "%BasePath%\AD\mods\folder2\Subfolder 2\doc1.txt"
    %SystemRoot%\System32\attrib.exe +h "%BasePath%\AD\mods\folder2\Empty subfolder 1"
    %SystemRoot%\System32\attrib.exe +r +s +h "%BasePath%\AD\mods\folder3"
    endlocal
    

    The deletion of the folder G:\AD\mods\folder3 is tricky as it has the read-only attribute set (and the system and hidden attribute) which results in the error message Access is denied. on using the command rd %1 2>nul after the first FOR loop in subroutine DeleteEmptyFolders which would otherwise delete only empty folders. For that reason a second FOR loop is used running once again DIR in a separate command process in background. The subroutine is exited if the directory is not empty because of still containing a file or a folder. Otherwise the folder is empty and rd /Q /S %1 is used to delete the empty folder even on having the read-only attribute set.


    To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.

    • call /? ... explains also %~dp0 ... drive and path of argument 0 which is the batch file path always ending with a backslash.
    • del /?
    • dir /?
    • echo /?
    • endlocal /?
    • findstr /?
    • for /?
    • goto /?
    • if /?
    • rd /?
    • setlocal /?

    Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on the FOR command lines to be interpreted as literal characters when Windows command interpreter processes the command line before executing command FOR which executes the embedded dir command line with findstr with using a separate command process started in background with %ComSpec% /c and the embedded command line appended as additional arguments.