Usunąć katalog z plikami?


246

Zastanawiam się, jaki jest najłatwiejszy sposób na usunięcie katalogu ze wszystkimi jego plikami?

Używam rmdir(PATH . '/' . $value);do usuwania folderu, jednak jeśli są w nim pliki, po prostu nie mogę go usunąć.



2
tak, odpowiedział dokładnie w tym pytaniu.
timdev

Chcę tylko zauważyć. Utworzyłem wiele plików i jeśli podczas tego procesu pojawi się błąd, to muszę usunąć wcześniej utworzone pliki. Po utworzeniu plików zapomniałem użyć, fclose($create_file);a po usunięciu dostałem Warning: unlink(created_file.xml): Permission denied in.... Aby uniknąć takich błędów, należy zamknąć utworzone pliki.
Andris

Odpowiedzi:


382

Obecnie dostępne są co najmniej dwie opcje.

  1. Przed usunięciem folderu usuń wszystkie jego pliki i foldery (a to oznacza rekurencję!). Oto przykład:

    public static function deleteDir($dirPath) {
        if (! is_dir($dirPath)) {
            throw new InvalidArgumentException("$dirPath must be a directory");
        }
        if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
            $dirPath .= '/';
        }
        $files = glob($dirPath . '*', GLOB_MARK);
        foreach ($files as $file) {
            if (is_dir($file)) {
                self::deleteDir($file);
            } else {
                unlink($file);
            }
        }
        rmdir($dirPath);
    }
  2. A jeśli używasz wersji 5.2+, możesz użyć RecursiveIterator, aby to zrobić bez samodzielnego wdrażania rekursji:

    $dir = 'samples' . DIRECTORY_SEPARATOR . 'sampledirtree';
    $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
    $files = new RecursiveIteratorIterator($it,
                 RecursiveIteratorIterator::CHILD_FIRST);
    foreach($files as $file) {
        if ($file->isDir()){
            rmdir($file->getRealPath());
        } else {
            unlink($file->getRealPath());
        }
    }
    rmdir($dir);

11
Druga implementacja jest nieco niebezpieczna: nie sprawdza kropek ( .i ..) i usuwa rozwiązaną ścieżkę, a nie rzeczywistą.
Alix Axel

9
mały dodatek :-) glob () nie obsługuje plików takich jak .htaccess. Użyłem tej funkcji do wyczyszczenia katalogów utworzonych przez KCFinder (wtyczka CKEditor), która generuje zarówno .htaccess, jak i .thumbs (plik + folder). Zamiast tego użyłem tej scandirfunkcji, aby uzyskać listę folderów. Upewnij się tylko, że filtrujesz „.” i pliki „..” z listy wyników.
Joshua - Pendo

25
DIRECTORY_SEPARATOR nie jest konieczny, gdy budujesz ścieżki do wysłania do systemu operacyjnego. Windows akceptuje również ukośniki. Jest to głównie przydatne do explode()wprowadzania ścieżki pobranej z systemu operacyjnego. alanhogan.com/tips/php/directory-separator-not-necessary
ReactiveRaven

5
Oprócz @Alix Axel Użycie tutaj [SplFileInfo :: getRealPath ()] ( php.net/manual/en/splfileinfo.getrealpath.php ) nie jest dobrym pomysłem. Ta metoda rozszerza wszystkie dowiązania symboliczne, co oznacza, że ​​zostanie usunięty gdzieś prawdziwy plik zamiast dowiązania symbolicznego z katalogu docelowego. Zamiast tego powinieneś użyć SplFileInfo :: getPathname ().
Vijit,

2
Zgadzam się z @Vijit, używam getPathname () zamiast getRealPath (). Robi to samo, nie usuwając więcej niż oczekujesz, jeśli zostaną znalezione dowiązania symboliczne.
JoeMoe1984

196

Zwykle używam tego do usuwania wszystkich plików w folderze:

array_map('unlink', glob("$dirname/*.*"));

I wtedy możesz zrobić

rmdir($dirname);

27
Nie powoduje to rekurencyjnego usuwania folderów; działa tylko wtedy, gdy folder zawiera tylko zwykłe pliki, z których wszystkie mają rozszerzenia plików.
mgnb

5
Jeśli nie jest wymagana rekurencja, jest to najlepsza i najprostsza jak dotąd odpowiedź. Dzięki!
eisbehr

2
Aby usunąć wszystkie pliki z folderu, nie tylko te z rozszerzeniami, użyj glob w następujący sposób: array_map('unlink', glob("$dirname/*"));To wciąż nie pozwala na usunięcie katalogów zagnieżdżonych w folderze.
kremuwa

Pamiętaj, że spowoduje to również usunięcie plików (ukrytych).
BadHorsie,

84

jaki jest najprostszy sposób na usunięcie katalogu zawierającego wszystkie pliki?

system("rm -rf ".escapeshellarg($dir));

33
Mam nadzieję, że nie mówisz poważnie. Co się stanie, jeśli $ dir jest /
The Pixel Developer

108
@ Dokładnie tak samo, jak w przypadku każdego z powyższych kodów. Czyż nie
Twój zdrowy rozsądek,

7
Pamiętaj, że w zależności od sposobu $dirgenerowania / udostępniania może być konieczne wykonanie dodatkowego przetwarzania wstępnego, aby zachować bezpieczeństwo i uniknąć błędów. Na przykład, jeśli $dirmoże mieć nieskalowaną przestrzeń lub średnik, mogą wystąpić niepożądane skutki uboczne. Nie jest tak w przypadku odpowiedzi, które używają takich rzeczy, rmdir()ponieważ będą one obsługiwać znaki specjalne dla Ciebie.
Trott,

5
Wersja systemu Windows:system('rmdir '.escapeshellarg($path).' /s /q');
Cypher

2
@ThePixelDeveloper nie powinieneś martwić się usunięciem /, to zadziałałoby tylko, gdybyś odsunął skrypt w linii poleceń jako root, ponieważ w sieci wszystko dzieje się jako użytkownik apache
Ben

49

Krótka funkcja, która wykonuje zadanie:

function deleteDir($path) {
    return is_file($path) ?
            @unlink($path) :
            array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
}

Używam go w klasie Utils, takiej jak ta:

class Utils {
    public static function deleteDir($path) {
        $class_func = array(__CLASS__, __FUNCTION__);
        return is_file($path) ?
                @unlink($path) :
                array_map($class_func, glob($path.'/*')) == @rmdir($path);
    }
}

Z wielką mocą wiąże się wielka odpowiedzialność : wywołanie tej funkcji pustą wartością spowoduje usunięcie plików zaczynających się w root ( /). Jako zabezpieczenie możesz sprawdzić, czy ścieżka jest pusta:

function deleteDir($path) {
    if (empty($path)) { 
        return false;
    }
    return is_file($path) ?
            @unlink($path) :
            array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
}

1
Statyczny nie działa, ponieważ $ this === NULL, gdy wywołujesz funkcję statyczną w klasie. To by działało, gdyby$this_func = array(__CLASS__, __FUNCTION__);
Matt Connolly

2
Czy ktoś może wyjaśnić linię array_map($class_func, glob($path.'/*')) == @rmdir($path)? Chyba rekurencyjnie przegląda podfoldery, ale co robi część == @rmdir? W jaki sposób <tablica boolanów> == <boolean> zwraca odpowiedź? Czy sprawdza, czy każda zwracana wartość rekurencji jest taka sama jak wartość logiczna po prawej stronie?
arviman

2
Sztuczka polega na połączeniu dwóch instrukcji w jedną instrukcję. Wynika to z faktu, że operatory trójskładnikowe dopuszczają tylko jedną instrukcję na argument. array_map(...)usuwa wszystkie pliki w katalogu, @rmdir(...)usuwa sam katalog.
Blaise

3
Bądź ostrożny! Ta funkcja nie sprawdza, czy ścieżka naprawdę istnieje. Jeśli podasz pusty argument, funkcja zacznie usuwać pliki zaczynając od katalogu głównego! Przed uruchomieniem tej funkcji dodaj kontrolę poczytalności do swojej ścieżki.
Tatu Ulmanen

3
Niektóre osoby nie widziały komentarza Tatu i rekursywnie były usuwane /, więc dołączyłem do mojego posta zabezpieczoną wersję.
Blaise

22

Jak widać w najczęściej głosowanych komentarzach na stronie podręcznika PHP o rmdir()(patrz http://php.net/manual/es/function.rmdir.php ), glob()funkcja nie zwraca ukrytych plików. scandir()stanowi alternatywę, która rozwiązuje ten problem.

Opisany tam algorytm (który w moim przypadku działał jak urok) to:

<?php 
    function delTree($dir)
    { 
        $files = array_diff(scandir($dir), array('.', '..')); 

        foreach ($files as $file) { 
            (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file"); 
        }

        return rmdir($dir); 
    } 
?>

czy możesz wyjaśnić is_dir („$ dir / $ file”) - nie spotkał się z parametrem „$ dir / $ file”
Igor L.,

Co masz na myśli? Sprawdza, czy pozycja znaleziona w katalogu ( $file) jest katalogiem lub plikiem. "$dir/$file"jest taki sam jak $dir . "/" . $file.
Niemiecki Latorre

Szczerze mówiąc, nie wiedziałem, że można łączyć takie zmienne :) thx
Igor L.

18

To krótsza wersja działa dla mnie świetnie

function deleteDirectory($dirPath) {
    if (is_dir($dirPath)) {
        $objects = scandir($dirPath);
        foreach ($objects as $object) {
            if ($object != "." && $object !="..") {
                if (filetype($dirPath . DIRECTORY_SEPARATOR . $object) == "dir") {
                    deleteDirectory($dirPath . DIRECTORY_SEPARATOR . $object);
                } else {
                    unlink($dirPath . DIRECTORY_SEPARATOR . $object);
                }
            }
        }
    reset($objects);
    rmdir($dirPath);
    }
}

15

Możesz użyć systemu plików Symfony ( kod ):

// composer require symfony/filesystem

use Symfony\Component\Filesystem\Filesystem;

(new Filesystem)->remove($dir);

Jednak nie mogłem usunąć niektórych złożonych struktur katalogów za pomocą tej metody, więc najpierw powinieneś spróbować, aby upewnić się, że działa poprawnie.


Mógłbym usunąć wspomnianą strukturę katalogów za pomocą implementacji specyficznej dla systemu Windows:

$dir = strtr($dir, '/', '\\');
// quotes are important, otherwise one could
// delete "foo" instead of "foo bar"
system('RMDIR /S /Q "'.$dir.'"');


I tylko dla kompletności, oto mój stary kod:

function xrmdir($dir) {
    $items = scandir($dir);
    foreach ($items as $item) {
        if ($item === '.' || $item === '..') {
            continue;
        }
        $path = $dir.'/'.$item;
        if (is_dir($path)) {
            xrmdir($path);
        } else {
            unlink($path);
        }
    }
    rmdir($dir);
}

Wielkie dzięki. Oszczędzasz mój czas.
zarif khan

„Nie wymyślaj koła ponownie” . Dziękuję
Kamafeather

9

Oto jedna przyjemna i prosta rekursja do usunięcia wszystkich plików w katalogu źródłowym, w tym katalogu:

function delete_dir($src) { 
    $dir = opendir($src);
    while(false !== ( $file = readdir($dir)) ) { 
        if (( $file != '.' ) && ( $file != '..' )) { 
            if ( is_dir($src . '/' . $file) ) { 
                delete_dir($src . '/' . $file); 
            } 
            else { 
                unlink($src . '/' . $file); 
            } 
        } 
    } 
    closedir($dir); 
    rmdir($src);

}

Funkcja opiera się na rekursji wykonanej dla kopiowania katalogu. Możesz znaleźć tę funkcję tutaj: Skopiuj całą zawartość katalogu do innego za pomocą php


4

Najlepsze rozwiązanie dla mnie

my_folder_delete("../path/folder");

kod:

function my_folder_delete($path) {
    if(!empty($path) && is_dir($path) ){
        $dir  = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS); //upper dirs are not included,otherwise DISASTER HAPPENS :)
        $files = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($files as $f) {if (is_file($f)) {unlink($f);} else {$empty_dirs[] = $f;} } if (!empty($empty_dirs)) {foreach ($empty_dirs as $eachDir) {rmdir($eachDir);}} rmdir($path);
    }
}

ps PAMIĘTAJ!
nie przekazuj PUSTEJ WARTOŚCI do żadnych funkcji usuwania katalogu !!! (zawsze wykonuj kopie zapasowe, w przeciwnym razie pewnego dnia możesz dostać katastrofę !!)


4

A co z tym:

function recursiveDelete($dirPath, $deleteParent = true){
    foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) {
        $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname());
    }
    if($deleteParent) rmdir($dirPath);
}

4

Funkcja Glob nie zwraca ukrytych plików, dlatego scandir może być bardziej przydatny podczas próby rekurencyjnego usunięcia drzewa.

<?php
public static function delTree($dir) {
   $files = array_diff(scandir($dir), array('.','..'));
    foreach ($files as $file) {
      (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file");
    }
    return rmdir($dir);
  }
?>

4

Możesz spróbować wykonać następujące czynności:

/*
 * Remove the directory and its content (all files and subdirectories).
 * @param string $dir the directory name
 */
function rmrf($dir) {
    foreach (glob($dir) as $file) {
        if (is_dir($file)) { 
            rmrf("$file/*");
            rmdir($file);
        } else {
            unlink($file);
        }
    }
}

3

Wolę to, ponieważ nadal zwraca PRAWDA, gdy się powiedzie, i FAŁSZ, gdy się nie powiedzie, a także zapobiega błędowi, w którym pusta ścieżka może próbować usunąć wszystko z „/ *” !!:

function deleteDir($path)
{
    return !empty($path) && is_file($path) ?
        @unlink($path) :
        (array_reduce(glob($path.'/*'), function ($r, $i) { return $r && deleteDir($i); }, TRUE)) && @rmdir($path);
}

3

Chcę rozwinąć odpowiedź autorstwa @alcuadrado z komentarzem @Vijit dotyczącym obsługi dowiązań symbolicznych. Po pierwsze, użyj getRealPath (). Ale jeśli masz jakieś dowiązania symboliczne, które są folderami, to się nie powiedzie, ponieważ spróbuje wywołać rmdir na link - więc potrzebujesz dodatkowej kontroli.

$it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
foreach($files as $file) {
    if ($file->isLink()) {
        unlink($file->getPathname());
    } else if ($file->isDir()){
        rmdir($file->getPathname());
    } else {
        unlink($file->getPathname());
    }
}
rmdir($dir);

1
Nie mam wystarczającej liczby przedstawicieli, aby bezpośrednio skomentować odpowiedź.
user701152,

3

Korzystanie z DirectoryIterator odpowiada poprzedniej odpowiedzi…

function deleteFolder($rootPath)
{   
    foreach(new DirectoryIterator($rootPath) as $fileToDelete)
    {
        if($fileToDelete->isDot()) continue;
        if ($fileToDelete->isFile())
            unlink($fileToDelete->getPathName());
        if ($fileToDelete->isDir())
            deleteFolder($fileToDelete->getPathName());
    }

    rmdir($rootPath);
}

3

Ten działa dla mnie:

function removeDirectory($path) {
    $files = glob($path . '/*');
    foreach ($files as $file) {
        is_dir($file) ? removeDirectory($file) : unlink($file);
    }
    rmdir($path);
    return;
}

2

Coś takiego?

function delete_folder($folder) {
    $glob = glob($folder);
    foreach ($glob as $g) {
        if (!is_dir($g)) {
            unlink($g);
        } else {
            delete_folder("$g/*");
            rmdir($g);
        }
    }
}

2

Mała modyfikacja kodu alcuadrado - globnie widzę plików o nazwach z punktów takich jak, .htaccesswięc używam skandiru, a skrypt usuwa się sam - sprawdź __FILE__.

function deleteDir($dirPath) {
    if (!is_dir($dirPath)) {
        throw new InvalidArgumentException("$dirPath must be a directory");
    }
    if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
        $dirPath .= '/';
    }
    $files = scandir($dirPath); 
    foreach ($files as $file) {
        if ($file === '.' || $file === '..') continue;
        if (is_dir($dirPath.$file)) {
            deleteDir($dirPath.$file);
        } else {
            if ($dirPath.$file !== __FILE__) {
                unlink($dirPath.$file);
            }
        }
    }
    rmdir($dirPath);
}

2

Przykład dla serwera Linux: exec('rm -f -r ' . $cache_folder . '/*');


Zwykle lubię dodawać kontrolę poprawności do $ cache_folder przed uruchomieniem rm -rf, aby uniknąć kosztownych błędów
glif

1

Usuń wszystkie pliki w folderze
array_map('unlink', glob("$directory/*.*"));
Usuń wszystko. * - Pliki w folderze (bez: „.” I „..”)
array_map('unlink', array_diff(glob("$directory/.*),array("$directory/.","$directory/..")))
Teraz usuń sam folder
rmdir($directory)


1

2 centy, aby dodać do tej odpowiedzi powyższej , co jest świetne BTW

Po zeskanowaniu / przeczytaniu katalogu przez funkcję glob (lub podobną) dodaj warunek, aby sprawdzić, czy odpowiedź nie jest pusta lub zostanie invalid argument supplied for foreach()wyświetlone ostrzeżenie. Więc...

if( ! empty( $files ) )
{
    foreach( $files as $file )
    {
        // do your stuff here...
    }
}

Moja pełna funkcja (jako metoda obiektowa):

    private function recursiveRemoveDirectory( $directory )
    {
        if( ! is_dir( $directory ) )
        {
            throw new InvalidArgumentException( "$directory must be a directory" );
        }

        if( substr( $directory, strlen( $directory ) - 1, 1 ) != '/' )
        {
            $directory .= '/';
        }

        $files = glob( $directory . "*" );

        if( ! empty( $files ) )
        {
            foreach( $files as $file )
            {
                if( is_dir( $file ) )
                {
                    $this->recursiveRemoveDirectory( $file );
                }
                else
                {
                    unlink( $file );
                }
            }               
        }
        rmdir( $directory );

    } // END recursiveRemoveDirectory()

1

Oto rozwiązanie, które działa idealnie:

function unlink_r($from) {
    if (!file_exists($from)) {return false;}
    $dir = opendir($from);
    while (false !== ($file = readdir($dir))) {
        if ($file == '.' OR $file == '..') {continue;}

        if (is_dir($from . DIRECTORY_SEPARATOR . $file)) {
            unlink_r($from . DIRECTORY_SEPARATOR . $file);
        }
        else {
            unlink($from . DIRECTORY_SEPARATOR . $file);
        }
    }
    rmdir($from);
    closedir($dir);
    return true;
}

1

Możesz skopiować pomocników YII

$ katalog (ciąg) - do rekurencyjnego usunięcia.

$ options (array) - do usunięcia katalogu. Prawidłowe opcje to: traverseSymlinks: boolean, czy dowiązania symboliczne do katalogów również powinny być przechodzące. Domyślnie falseoznacza to, że zawartość dowiązania symbolicznego nie zostanie usunięta. W takim przypadku domyślnym zostanie usunięte tylko dowiązanie symboliczne.

public static function removeDirectory($directory,$options=array())
{
    if(!isset($options['traverseSymlinks']))
        $options['traverseSymlinks']=false;
    $items=glob($directory.DIRECTORY_SEPARATOR.'{,.}*',GLOB_MARK | GLOB_BRACE);
    foreach($items as $item)
    {
        if(basename($item)=='.' || basename($item)=='..')
            continue;
        if(substr($item,-1)==DIRECTORY_SEPARATOR)
        {
            if(!$options['traverseSymlinks'] && is_link(rtrim($item,DIRECTORY_SEPARATOR)))
                unlink(rtrim($item,DIRECTORY_SEPARATOR));
            else
                self::removeDirectory($item,$options);
        }
        else
            unlink($item);
    }
    if(is_dir($directory=rtrim($directory,'\\/')))
    {
        if(is_link($directory))
            unlink($directory);
        else
            rmdir($directory);
    }
}

0
<?php
  function rrmdir($dir) {
  if (is_dir($dir)) {
    $objects = scandir($dir);
    foreach ($objects as $object) {
      if ($object != "." && $object != "..") {
        if (filetype($dir."/".$object) == "dir") 
           rrmdir($dir."/".$object); 
        else unlink   ($dir."/".$object);
      }
    }
    reset($objects);
    rmdir($dir);
  }
 }
?>

Spróbuj wypróbować poniższy kod z php.net

Pracuj dla mnie dobrze


0

Dla Windowsa:

system("rmdir ".escapeshellarg($path) . " /s /q");

0

Podobnie jak rozwiązanie Playnox, ale z eleganckim wbudowanym DirectoryIterator:

function delete_directory($dirPath){
 if(is_dir($dirPath)){
  $objects=new DirectoryIterator($dirPath);
   foreach ($objects as $object){
    if(!$object->isDot()){
     if($object->isDir()){
      delete_directory($object->getPathname());
     }else{
      unlink($object->getPathname());
     }
    }
   }
   rmdir($dirPath);
  }else{
   throw new Exception(__FUNCTION__.'(dirPath): dirPath is not a directory!');
  }
 }

0

Nie pamiętam, skąd skopiowałem tę funkcję, ale wygląda na to, że nie ma jej na liście i działa dla mnie

function rm_rf($path) {
    if (@is_dir($path) && is_writable($path)) {
        $dp = opendir($path);
        while ($ent = readdir($dp)) {
            if ($ent == '.' || $ent == '..') {
                continue;
            }
            $file = $path . DIRECTORY_SEPARATOR . $ent;
            if (@is_dir($file)) {
                rm_rf($file);
            } elseif (is_writable($file)) {
                unlink($file);
            } else {
                echo $file . "is not writable and cannot be removed. Please fix the permission or select a new path.\n";
            }
        }
        closedir($dp);
        return rmdir($path);
    } else {
        return @unlink($path);
    }
}

0

Proste i łatwe...

$dir ='pathtodir';
if (is_dir($dir)) {
  foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename) {
    if ($filename->isDir()) continue;
    unlink($filename);
  }
  rmdir($dir);
}


0

Jeśli nie masz pewności, podana ścieżka to katalog lub plik, możesz użyć tej funkcji do usunięcia ścieżki

function deletePath($path) {
        if(is_file($path)){
            unlink($path);
        } elseif(is_dir($path)){
            $path = (substr($path, -1) !== DIRECTORY_SEPARATOR) ? $path . DIRECTORY_SEPARATOR : $path;
            $files = glob($path . '*');
            foreach ($files as $file) {
                deleteDirPath($file);
            }
            rmdir($path);
        } else {
            return false;
        }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.