programing

Java에서 반복적으로 디렉토리 삭제

newsource 2022. 8. 12. 23:29

Java에서 반복적으로 디렉토리 삭제

Java에서 디렉토리 전체를 재귀적으로 삭제할 수 있는 방법이 있습니까?

통상의 경우, 빈 디렉토리를 삭제할 수 있습니다.그러나 콘텐츠가 포함된 전체 디렉토리를 삭제하는 것은 더 이상 간단하지 않습니다.

Java에서 내용이 포함된 전체 디렉토리를 삭제하려면 어떻게 해야 합니까?

아파치의 공통점-IO를 확인해 보세요.원하는 작업을 수행하는 FileUtils 클래스가 있습니다.

FileUtils.deleteDirectory(new File("directory"));

Java 7을 사용하면 신뢰할 수 있는 심볼링크 검출을 통해 이를 실현할 수 있습니다.(Apache의 Commons-io는 현재 신뢰할 수 있는 심볼링크 검출 기능을 갖추고 있지 않다고 생각합니다.이는 Apache가 Windows에서 작성한 링크를 처리하지 않기 때문입니다.mklink

역사를 위해 Java 7 이전 답변과 심볼링크를 소개합니다.

void delete(File f) throws IOException {
  if (f.isDirectory()) {
    for (File c : f.listFiles())
      delete(c);
  }
  if (!f.delete())
    throw new FileNotFoundException("Failed to delete file: " + f);
}

Java 7+에서는 클래스를 사용할 수 있습니다.코드는 매우 간단합니다.

Path directory = Paths.get("/tmp");
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
   @Override
   public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
       Files.delete(file);
       return FileVisitResult.CONTINUE;
   }

   @Override
   public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
       Files.delete(dir);
       return FileVisitResult.CONTINUE;
   }
});

시작 디렉토리를 포함한 모든 파일과 디렉토리를 재귀적으로 삭제하는 원라이너 솔루션(Java8)

Files.walk(Paths.get("c:/dir_to_delete/"))
                .map(Path::toFile)
                .sorted((o1, o2) -> -o1.compareTo(o2))
                .forEach(File::delete);

역순서에는 비교기를 사용합니다.그렇지 않으면 파일::delete는 비어 있지 않은 디렉토리를 삭제할 수 없습니다.따라서 디렉토리를 유지하고 파일만 삭제하려면 sorted()에서 비교기를 삭제하거나 정렬을 완전히 삭제하고 파일필터를 추가합니다.

Files.walk(Paths.get("c:/dir_to_delete/"))
                .filter(Files::isRegularFile)
                .map(Path::toFile)
                .forEach(File::delete);

Java 7은 심볼링크 처리 기능이 있는 워킹 디렉토리에 대한 지원을 추가했습니다.

import java.nio.file.*;

public static void removeRecursive(Path path) throws IOException
{
    Files.walkFileTree(path, new SimpleFileVisitor<Path>()
    {
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException
        {
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
        {
            // try to delete the file anyway, even if its attributes
            // could not be read, since delete-only access is
            // theoretically possible
            Files.delete(file);
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
        {
            if (exc == null)
            {
                Files.delete(dir);
                return FileVisitResult.CONTINUE;
            }
            else
            {
                // directory iteration failed; propagate exception
                throw exc;
            }
        }
    });
}

플랫폼 고유의 메서드로부터의 폴백으로서 이것을 사용합니다(이 테스트되지 않은 코드에서는).

public static void removeDirectory(Path directory) throws IOException
{
    // does nothing if non-existent
    if (Files.exists(directory))
    {
        try
        {
            // prefer OS-dependent directory removal tool
            if (SystemUtils.IS_OS_WINDOWS)
                Processes.execute("%ComSpec%", "/C", "RD /S /Q \"" + directory + '"');
            else if (SystemUtils.IS_OS_UNIX)
                Processes.execute("/bin/rm", "-rf", directory.toString());
        }
        catch (ProcessExecutionException | InterruptedException e)
        {
            // fallback to internal implementation on error
        }

        if (Files.exists(directory))
            removeRecursive(directory);
    }
}

(System Utils는 Apache Commons Lang에서 왔습니다.프로세스는 비공개이지만 그 동작은 명백해야 합니다.)

제 솔루션은 에릭슨 솔루션과 거의 비슷하지만 정적 방법으로 패키징되어 있습니다.이것을 어딘가에 떨어뜨리면, Apache Commons를 모두 설치하는 것보다 훨씬 가볍습니다(보시는 것처럼).

public class FileUtils {
    /**
     * By default File#delete fails for non-empty directories, it works like "rm". 
     * We need something a little more brutual - this does the equivalent of "rm -r"
     * @param path Root File Path
     * @return true iff the file and all sub files/directories have been removed
     * @throws FileNotFoundException
     */
    public static boolean deleteRecursive(File path) throws FileNotFoundException{
        if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath());
        boolean ret = true;
        if (path.isDirectory()){
            for (File f : path.listFiles()){
                ret = ret && deleteRecursive(f);
            }
        }
        return ret && path.delete();
    }
}

스택이 있고 재귀 메서드가 없는 솔루션:

File dir = new File("/path/to/dir");
File[] currList;
Stack<File> stack = new Stack<File>();
stack.push(dir);
while (! stack.isEmpty()) {
    if (stack.lastElement().isDirectory()) {
        currList = stack.lastElement().listFiles();
        if (currList.length > 0) {
            for (File curr: currList) {
                stack.push(curr);
            }
        } else {
            stack.pop().delete();
        }
    } else {
        stack.pop().delete();
    }
}

Spring을 사용하는 경우 FileSystemUtils.deleteRecursive를 사용할 수 있습니다.

import org.springframework.util.FileSystemUtils;

boolean success = FileSystemUtils.deleteRecursively(new File("directory"));

구아바구아바 9일까지 지원했다.

Guava 10부터:

권장되지 않습니다.이 메서드는 심볼링크 검출 불량과 레이스 조건에 시달립니다.이 기능은 다음과 같은 운영 체제명령어를 실행해야만 적절히 지원할 수 있습니다.rm -rf ★★★★★★★★★★★★★★★★★」del /s이 메서드는 Guava 릴리즈 11.0에서 Guava에서 삭제될 예정입니다.

따라서 Guava 11에는 이러한 방법이 없습니다.

for(Path p : Files.walk(directoryToDelete).
        sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
        toArray(Path[]::new))
{
    Files.delete(p);
}

, 「 」를 는, 「 」를 해 주세요.IOException:

Files.walk(directoryToDelete).
    sorted((a, b) -> b.compareTo(a)). // reverse; files before dirs
    forEach(p -> {
        try { Files.delete(p); }
        catch(IOException e) { /* ... */ }
      });
public void deleteRecursive(File path){
    File[] c = path.listFiles();
    System.out.println("Cleaning out folder:" + path.toString());
    for (File file : c){
        if (file.isDirectory()){
            System.out.println("Deleting file:" + file.toString());
            deleteRecursive(file);
            file.delete();
        } else {
            file.delete();
        }
    }
    path.delete();
}
public static void deleteDirectory(File path) 
{
    if (path == null)
        return;
    if (path.exists())
    {
        for(File f : path.listFiles())
        {
            if(f.isDirectory()) 
            {
                deleteDirectory(f);
                f.delete();
            }
            else
            {
                f.delete();
            }
        }
        path.delete();
    }
}

symblinks와 위의 코드에서 실패하는 두 가지 방법...해결책을 모르기 때문입니다.

방법 #1

테스트를 작성하려면 다음을 수행합니다.

echo test > testfile
mkdir dirtodelete
ln -s badlink dirtodelete/badlinktodelete

테스트 파일과 테스트 디렉토리가 표시됩니다.

$ ls testfile dirtodelete
testfile

dirtodelete:
linktodelete

다음으로 commons-io delete Directory()를 실행합니다.파일을 찾을 수 없다는 메시지가 표시되며 크래시됩니다.다른 예는 여기서 무엇을 하는지 잘 모르겠습니다.Linux rm 명령어는 단순히 링크를 삭제하고 디렉토리 상의 rm -r도 삭제합니다.

Exception in thread "main" java.io.FileNotFoundException: File does not exist: /tmp/dirtodelete/linktodelete

방법 #2

테스트를 작성하려면 다음을 수행합니다.

mkdir testdir
echo test > testdir/testfile
mkdir dirtodelete
ln -s ../testdir dirtodelete/dirlinktodelete

테스트 파일과 테스트 디렉토리가 표시됩니다.

$ ls dirtodelete testdir
dirtodelete:
dirlinktodelete

testdir:
testfile

그런 다음 commons-io delete Directory() 또는 사람들이 게시한 코드 예를 실행합니다.디렉토리뿐만 아니라 삭제할 디렉토리 외부에 있는 테스트파일도 삭제됩니다.(디렉토리를 암묵적으로 폐기하고 내용을 삭제합니다).rm - r은 링크만 삭제합니다."find -L dutodelete -type f -exec rm {} \;"과 같은 참조 파일을 삭제해야 합니다.

$ ls dirtodelete testdir
ls: cannot access dirtodelete: No such file or directory
testdir:

다음을 사용할 수 있습니다.

org.apache.commons.io.FileUtils.deleteQuietly(destFile);

파일을 삭제하고 예외를 발생시키지 않습니다.파일이 디렉토리인 경우는, 그 파일과 모든 서브 디렉토리를 삭제합니다.File.delete()와 이 메서드의 차이점은 삭제할 디렉토리는 비워둘 필요가 없다는 것입니다.파일 또는 디렉토리를 삭제할 수 없는 경우 예외가 발생하지 않습니다.

메서드에서 발생하는 예외는 항상 그 메서드가 무엇을 시도했는지(그리고 실패했는지) 기술해야 한다는 접근법과 일관되게 예외를 처리하는 최적의 솔루션:

private void deleteRecursive(File f) throws Exception {
    try {
        if (f.isDirectory()) {
            for (File c : f.listFiles()) {
                deleteRecursive(c);
            }
        }
        if (!f.delete()) {
            throw new Exception("Delete command returned false for file: " + f);
        }
    } 
    catch (Exception e) {
        throw new Exception("Failed to delete the folder: " + f, e);
    }
}

레거시 프로젝트에서는 네이티브 Java 코드를 생성해야 합니다.저는 이 코드를 Paulitex 코드와 비슷하게 만듭니다.다음을 참조하십시오.

public class FileHelper {

   public static boolean delete(File fileOrFolder) {
      boolean result = true;
      if(fileOrFolder.isDirectory()) {
         for (File file : fileOrFolder.listFiles()) {
            result = result && delete(file);
         }
      }
      result = result && fileOrFolder.delete();
      return result;
   } 
}

그리고 단위 테스트:

public class FileHelperTest {

    @Before
    public void setup() throws IOException {
       new File("FOLDER_TO_DELETE/SUBFOLDER").mkdirs();
       new File("FOLDER_TO_DELETE/SUBFOLDER_TWO").mkdirs();
       new File("FOLDER_TO_DELETE/SUBFOLDER_TWO/TEST_FILE.txt").createNewFile();
    }

    @Test
    public void deleteFolderWithFiles() {
       File folderToDelete = new File("FOLDER_TO_DELETE");
       Assert.assertTrue(FileHelper.delete(folderToDelete));
       Assert.assertFalse(new File("FOLDER_TO_DELETE").exists());
    }

}

아래 코드는 특정 폴더의 모든 내용을 반복적으로 삭제합니다.

boolean deleteDirectory(File directoryToBeDeleted) {
    File[] allContents = directoryToBeDeleted.listFiles();
    if (allContents != null) {
        for (File file : allContents) {
            deleteDirectory(file);
        }
    }
    return directoryToBeDeleted.delete();
}

다음은 명령줄 인수를 받아들이는 BARE BONES 메인 메서드입니다.자신의 에러 체크를 추가하거나, 필요에 따라서 커맨드 라인 인수를 성형할 필요가 있습니다.

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class DeleteFiles {

/**
 * @param intitial arguments take in a source to read from and a 
 * destination to read to
 */
    public static void main(String[] args)
                     throws FileNotFoundException,IOException {
        File src = new File(args[0]);
        if (!src.exists() ) {
            System.out.println("FAILURE!");
        }else{
            // Gathers files in directory
            File[] a = src.listFiles();
            for (int i = 0; i < a.length; i++) {
                //Sends files to recursive deletion method
                fileDelete(a[i]);
            }
            // Deletes original source folder
            src.delete();
            System.out.println("Success!");
        }
    }

    /**
     * @param srcFile Source file to examine
     * @throws FileNotFoundException if File not found
     * @throws IOException if File not found
     */
    private static void fileDelete(File srcFile)
                     throws FileNotFoundException, IOException {
        // Checks if file is a directory
        if (srcFile.isDirectory()) {
            //Gathers files in directory
            File[] b = srcFile.listFiles();
            for (int i = 0; i < b.length; i++) {
                //Recursively deletes all files and sub-directories
                fileDelete(b[i]);
            }
            // Deletes original sub-directory file
            srcFile.delete();
        } else {
            srcFile.delete();
        }
    }
}

도움이 됐으면 좋겠네요!

Guava는 다음과 같은 단일 라이너를 제공합니다.

공유되는 많은 예와 달리 심볼릭링크를 설명하며 제공된 경로 외부에 있는 파일은 기본적으로 삭제되지 않습니다.

이 문제의 해결 방법은 erickson의 답변에서 다음 코드를 사용하여 파일 클래스의 삭제 방법을 다시 구현하는 것입니다.

public class MyFile extends File {

  ... <- copy constructor

  public boolean delete() {
    if (f.isDirectory()) {
      for (File c : f.listFiles()) {
        return new MyFile(c).delete();
      }
    } else {
        return f.delete();
    }
  }
}

Commons IO 및 < Java SE 7 없음

public static void deleteRecursive(File path){
            path.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    if (pathname.isDirectory()) {
                        pathname.listFiles(this);
                        pathname.delete();
                    } else {
                        pathname.delete();
                    }
                    return false;
                }
            });
            path.delete();
        }

// Java 8 (lambda & stream 포함, param이 디렉토리인 경우)

static boolean delRecursive(File dir) {
    return Arrays.stream(dir.listFiles()).allMatch((f) -> f.isDirectory() ? delRecursive(f) : f.delete()) && dir.delete();
}

// param이 파일 또는 디렉토리인 경우

static boolean delRecursive(File fileOrDir) {
    return fileOrDir.isDirectory() ? Arrays.stream(fileOrDir.listFiles()).allMatch((f) -> delRecursive(f)) && fileOrDir.delete() : fileOrDir.delete();
}

file.delete()를 사용하여 파일을 쉽게 삭제할 수 있지만 디렉토리를 삭제하려면 비어 있어야 합니다.재귀 기능을 사용하면 이 작업을 쉽게 수행할 수 있습니다.예를 들어 다음과 같습니다.

public static void clearFolders(String[] args) {
        for(String st : args){
            File folder = new File(st);
            if (folder.isDirectory()) {
                File[] files = folder.listFiles();
                if(files!=null) { 
                    for(File f: files) {
                        if (f.isDirectory()){
                            clearFolders(new String[]{f.getAbsolutePath()});
                            f.delete();
                        } else {
                            f.delete();
                        }
                    }
                }
            }
        }
    }

안전한 사용을 위한 3가지 안전 기준을 가진 이 루틴을 코드화했습니다.

package ch.ethz.idsc.queuey.util;

import java.io.File;
import java.io.IOException;

/** recursive file/directory deletion
 * 
 * safety from erroneous use is enhanced by three criteria
 * 1) checking the depth of the directory tree T to be deleted
 * against a permitted upper bound "max_depth"
 * 2) checking the number of files to be deleted #F
 * against a permitted upper bound "max_count"
 * 3) if deletion of a file or directory fails, the process aborts */
public final class FileDelete {
    /** Example: The command
     * FileDelete.of(new File("/user/name/myapp/recordings/log20171024"), 2, 1000);
     * deletes given directory with sub directories of depth of at most 2,
     * and max number of total files less than 1000. No files are deleted
     * if directory tree exceeds 2, or total of files exceed 1000.
     * 
     * abort criteria are described at top of class
     * 
     * @param file
     * @param max_depth
     * @param max_count
     * @return
     * @throws Exception if criteria are not met */
    public static FileDelete of(File file, int max_depth, int max_count) throws IOException {
        return new FileDelete(file, max_depth, max_count);
    }

    // ---
    private final File root;
    private final int max_depth;
    private int removed = 0;

    /** @param root file or a directory. If root is a file, the file will be deleted.
     *            If root is a directory, the directory tree will be deleted.
     * @param max_depth of directory visitor
     * @param max_count of files to delete
     * @throws IOException */
    private FileDelete(final File root, final int max_depth, final int max_count) throws IOException {
        this.root = root;
        this.max_depth = max_depth;
        // ---
        final int count = visitRecursively(root, 0, false);
        if (count <= max_count) // abort criteria 2)
            visitRecursively(root, 0, true);
        else
            throw new IOException("more files to be deleted than allowed (" + max_count + "<=" + count + ") in " + root);
    }

    private int visitRecursively(final File file, final int depth, final boolean delete) throws IOException {
        if (max_depth < depth) // enforce depth limit, abort criteria 1)
            throw new IOException("directory tree exceeds permitted depth");
        // ---
        int count = 0;
        if (file.isDirectory()) // if file is a directory, recur
            for (File entry : file.listFiles())
                count += visitRecursively(entry, depth + 1, delete);
        ++count; // count file as visited
        if (delete) {
            final boolean deleted = file.delete();
            if (!deleted) // abort criteria 3)
                throw new IOException("cannot delete " + file.getAbsolutePath());
            ++removed;
        }
        return count;
    }

    public int deletedCount() {
        return removed;
    }

    public void printNotification() {
        int count = deletedCount();
        if (0 < count)
            System.out.println("deleted " + count + " file(s) in " + root);
    }
}

rm -rf보다 훨씬 퍼포먼스가 뛰어났다FileUtils.deleteDirectory.

광범위한 벤치마킹을 실시한 결과,rm -rf를 사용하는 것보다 몇 배나 빠릅니다.FileUtils.deleteDirectory.

물론, 작은 디렉토리나 단순한 디렉토리의 경우는 문제가 되지 않습니다만, 이 경우, 몇 기가바이트의 깊이 중첩된 서브 디렉토리는 10분 이상 걸립니다.FileUtils.deleteDirectory1분 동안만rm -rf.

이를 위한 Java의 대략적인 구현은 다음과 같습니다.

// Delete directory given and all subdirectories and files (i.e. recursively).
//
static public boolean deleteDirectory( File file ) throws IOException, InterruptedException {

    if ( file.exists() ) {

        String deleteCommand = "rm -rf " + file.getAbsolutePath();
        Runtime runtime = Runtime.getRuntime();

        Process process = runtime.exec( deleteCommand );
        process.waitFor();

        return true;
    }

    return false;

}

큰 디렉토리나 복잡한 디렉토리를 취급하는 경우는, 시도해 볼 가치가 있습니다.

자, 예를 들어보자.

import java.io.File;
import java.io.IOException;

public class DeleteDirectory
{
   private static final String folder = "D:/project/java";
 
   public static void main(String[] args) throws IOException
   {
      File fl = new File(folder);
      if(!fl.exists()) // checking if directory exists
      {
         System.out.println("Sorry!! directory doesn't exist.");
      }
      else
      {
         DeleteDirectory dd = new DeleteDirectory();
         dd.deleteDirectory(fl);
      }
   }
 
   public void deleteDirectory(File file) throws IOException
   {
      if(file.isDirectory())
      {
         if(file.list().length == 0)
         { 
            deleteEmptyDirectory(file); // here if directory is empty delete we are deleting
         }
         else
         {
            File fe[] = file.listFiles();
            for(File deleteFile : fe)
            {
               deleteDirectory(deleteFile); // recursive call
            }
            if(file.list().length == 0)
            {
               deleteEmptyDirectory(file);
            }
         }
      }
      else
      {
         file.delete();
         System.out.println("File deleted : " + file.getAbsolutePath());
      }
   }
 
   private void deleteEmptyDirectory(File fi)
   {
      fi.delete();
      System.out.println("Directory deleted : " + fi.getAbsolutePath());
   }
}

상세한 것에 대하여는, 아래의 자원을 참조해 주세요.

디렉토리 삭제

구아바 21.0 이후

이 있습니다.void deleteRecursively(Path path, RecursiveDeleteOption... options) throws IOException스태틱 메서드MoreFilesGuava 21.0 이후 이용 가능한 클래스입니다.

다음 Javadoc 문서를 참조하십시오.

public static void deleteRecursive(패스 경로, RecursiveDeleteOption... 옵션)가 IOException을 슬로우합니다.

지정된 파일 또는 디렉토리를 삭제합니다.path재귀적으로대상이 아닌 심볼릭링크를 삭제합니다(다음 주의사항이 있습니다).

지정된 디렉토리의 파일을 읽거나 열거나 삭제하려고 하면 I/O 예외가 발생하면 이 메서드는 해당 파일을 건너뛰고 계속 진행합니다.이러한 예외는 모두 수집되어 모든 파일을 삭제하려고 하면IOException이러한 예외는 억제된 예외로서 던져집니다.

언급URL : https://stackoverflow.com/questions/779519/delete-directories-recursively-in-java