Wie lösche ich rekursiv ein Verzeichnis und seinen gesamten Inhalt (Dateien + Unterverzeichnisse) in PHP?


Antworten:


207

Der vom Benutzer bereitgestellte Abschnitt auf der Handbuchseite von rmdirenthält eine anständige Implementierung:

 function rrmdir($dir) { 
   if (is_dir($dir)) { 
     $objects = scandir($dir);
     foreach ($objects as $object) { 
       if ($object != "." && $object != "..") { 
         if (is_dir($dir. DIRECTORY_SEPARATOR .$object) && !is_link($dir."/".$object))
           rrmdir($dir. DIRECTORY_SEPARATOR .$object);
         else
           unlink($dir. DIRECTORY_SEPARATOR .$object); 
       } 
     }
     rmdir($dir); 
   } 
 }

1
@ The Pixel Developer - Ich habe eine Antwort hinzugefügt , die das zeigt.
salathe

2
Schauen Sie sich die Lösung an, die mir jemand für dieselbe Frage gegeben hat: Glob scheint besser
NoodleOfDeath

Dies ruft is_dirzweimal für jedes rekursive Verzeichnis auf. Wenn das Argument ein Symlink ist, folgt es ihm auch, anstatt den Symlink zu löschen, der möglicherweise das ist, was Sie wollen oder nicht. Auf jeden Fall ist es nicht das, was rm -rftut.
Vladimir Panteleev

116

Aufbauend auf dem Kommentar des Pixel-Entwicklers könnte ein Ausschnitt mit der SPL folgendermaßen aussehen:

$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
    RecursiveIteratorIterator::CHILD_FIRST
);

foreach ($files as $fileinfo) {
    $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
    $todo($fileinfo->getRealPath());
}

rmdir($dir);

Hinweis: Es wird keine Überprüfung der Integrität durchgeführt und das mit dem FilesystemIterator in PHP 5.3.0 eingeführte Flag SKIP_DOTS verwendet. Natürlich $todokönnte das ein if/ sein else. Der wichtige Punkt ist, dass CHILD_FIRSTdie untergeordneten Elemente (Dateien) zuerst vor den übergeordneten Elementen (Ordnern) durchlaufen werden.


SKIP_DOTSwurde nur in PHP 5.3 eingeführt? Wo hast du das gesehen?
Alix Axel

Danke dir. Außerdem: Sollten Sie nicht die getPathname()Methode anstelle von verwenden getRealPath()?
Alix Axel

3
Diese Lösung funktioniert gut, löscht jedoch alles ... außer dem Verzeichnis (ob leer oder nicht). Es sollte ein rmdir($dir)am Ende des Skripts stehen.
Laurent

3
Hier ist dieselbe Funktion entpackt, doc-blockiert und konsistent gemacht mit rmdir()und unlink()z. B. abgebrochen E_WARNINGund zurückgegeben trueoder falseErfolg angezeigt .
mindplay.dk

2
@dbf nein wird es nicht, das FilesystemIteratorist kein rekursiver Iterator.
salathe

17

Löscht alle Dateien und Ordner im Pfad.

function recurseRmdir($dir) {
  $files = array_diff(scandir($dir), array('.','..'));
  foreach ($files as $file) {
    (is_dir("$dir/$file")) ? recurseRmdir("$dir/$file") : unlink("$dir/$file");
  }
  return rmdir($dir);
}

1
rm -rf /== recurseRmdir('/'):)
Aaron Esau

5
Bitte beachten Sie, dass dies nicht symlink-sicher ist! Sie benötigen nach is_dir eine Überprüfung der Integrität, um auch zu überprüfen, ob es sich um! Is_link handelt. Andernfalls können Sie einen Symlink zu einem externen Ordner erstellen, der dann gelöscht wird. Dies kann als Sicherheitslücke angesehen werden. Also sollten Sie is_dir("$dir/$file")zuis_dir("$dir/$file") && !is_link("$dir/$file")
Kira M. Backes

13

Für * nix können Sie ein shell_execfür rm -Roder DEL /S folder_namefür Windows verwenden.


2
Wie wäre es mit DEL /S folder_nameWindows
ankitjaininfo

@ Gordon RMDIR /S /Q folder_namehat für mich funktioniert
Brian Leishman

2
@ WiR3D Solange der Befehl exec keine Benutzereingaben enthält, sollten Sie gut sein. Beispiel:exec('rm -rf ' . __DIR__ . '/output/*.log');
Brian Hannay

5
<?php

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

# http://stackoverflow.com/a/3352564/283851
# https://gist.github.com/XzaR90/48c6b615be12fa765898

# Forked from https://gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2

/**
 * Recursively delete a directory and all of it's contents - e.g.the equivalent of `rm -r` on the command-line.
 * Consistent with `rmdir()` and `unlink()`, an E_WARNING level error will be generated on failure.
 *
 * @param string $source absolute path to directory or file to delete.
 * @param bool   $removeOnlyChildren set to true will only remove content inside directory.
 *
 * @return bool true on success; false on failure
 */
function rrmdir($source, $removeOnlyChildren = false)
{
    if(empty($source) || file_exists($source) === false)
    {
        return false;
    }

    if(is_file($source) || is_link($source))
    {
        return unlink($source);
    }

    $files = new RecursiveIteratorIterator
    (
        new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );

    //$fileinfo as SplFileInfo
    foreach($files as $fileinfo)
    {
        if($fileinfo->isDir())
        {
            if(rrmdir($fileinfo->getRealPath()) === false)
            {
                return false;
            }
        }
        else
        {
            if(unlink($fileinfo->getRealPath()) === false)
            {
                return false;
            }
        }
    }

    if($removeOnlyChildren === false)
    {
        return rmdir($source);
    }

    return true;
}

Ziemlich komplexer Vorschlag ;-)
Philipp

@Philipp ja ich denke. Nun, ich habe eine Gabel aus gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2 gemacht, weil ich es nicht zum Laufen gebracht habe, also dachte ich nur, ich könnte es auch teilen.
XzaR

Es gibt ein Problem. Leere Ordner werden nicht gelöscht, nachdem alle Dateien entfernt wurden. Poste leicht modifizierte Version als Antwort unten.
Vladislav Rastrusny

@ Vladislav Rastrusny wirklich? Für mich geht das. Vielleicht hatten Sie einen Ordner mit schreibgeschützt oder so.
XzaR


1

'einfacher' Code, der funktioniert und von einem Zehnjährigen gelesen werden kann:

function deleteNonEmptyDir($dir) 
{
   if (is_dir($dir)) 
   {
        $objects = scandir($dir);

        foreach ($objects as $object) 
        {
            if ($object != "." && $object != "..") 
            {
                if (filetype($dir . "/" . $object) == "dir")
                {
                    deleteNonEmptyDir($dir . "/" . $object); 
                }
                else
                {
                    unlink($dir . "/" . $object);
                }
            }
        }

        reset($objects);
        rmdir($dir);
    }
}

Bitte beachten Sie, dass ich die Lösung hier nur erweitert / vereinfacht und korrigiert habe (funktionierte nicht für nicht leere Verzeichnisse): Wie entferne ich in PHP rekursiv alle Ordner, die nicht leer sind?


1

Verbesserte @ Artefacto-Lösung - korrigierte Tippfehler und vereinfachter Code, die für beide funktionieren - leere und nicht leere Verzeichnisse.

  function recursive_rmdir($dir) { 
    if( is_dir($dir) ) { 
      $objects = array_diff( scandir($dir), array('..', '.') );
      foreach ($objects as $object) { 
        $objectPath = $dir."/".$object;
        if( is_dir($objectPath) )
          recursive_rmdir($objectPath);
        else
          unlink($objectPath); 
      } 
      rmdir($dir); 
    } 
  }

1

Die 100% funktionierende Lösung

public static function rmdir_recursive($directory, $delete_parent = null)
  {
    $files = glob($directory . '/{,.}[!.,!..]*',GLOB_MARK|GLOB_BRACE);
    foreach ($files as $file) {
      if (is_dir($file)) {
        self::rmdir_recursive($file, 1);
      } else {
        unlink($file);
      }
    }
    if ($delete_parent) {
      rmdir($directory);
    }
  }

0

Etwas wie das?

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

Ich kann nicht erklären warum, aber das hat bei mir nicht funktioniert. Es wurde immer wieder versucht, einen Ordner zu löschen, der nicht leer war. Die zweite Antwort oben hat gut funktioniert.
Laurent

1
@ buggy3 Auf welchen spezifischen Code beziehen Sie sich? Der Link verweist einfach auf diese Fragenseite.
Cgogolin

0

Beispiel mit der Funktion glob () . Alle Dateien und Ordner werden rekursiv gelöscht, einschließlich Dateien, die mit dot beginnen.

delete_all( 'folder' );

function delete_all( $item ) {
    if ( is_dir( $item ) ) {
        array_map( 'delete_all', array_diff( glob( "$item/{,.}*", GLOB_BRACE ), array( "$item/.", "$item/.." ) ) );
        rmdir( $item );
    } else {
        unlink( $item );
    }
};

Ich ging mitsystem('rm -fr folder')
Itay Moav -Malimovka

0

Die Funktion unlinkr löscht rekursiv alle Ordner und Dateien im angegebenen Pfad, indem sichergestellt wird, dass das Skript selbst nicht gelöscht wird.

function unlinkr($dir, $pattern = "*") {
    // find all files and folders matching pattern
    $files = glob($dir . "/$pattern"); 

    //interate thorugh the files and folders
    foreach($files as $file){ 
    //if it is a directory then re-call unlinkr function to delete files inside this directory     
        if (is_dir($file) and !in_array($file, array('..', '.')))  {
            echo "<p>opening directory $file </p>";
            unlinkr($file, $pattern);
            //remove the directory itself
            echo "<p> deleting directory $file </p>";
            rmdir($file);
        } else if(is_file($file) and ($file != __FILE__)) {
            // make sure you don't delete the current script
            echo "<p>deleting file $file </p>";
            unlink($file); 
        }
    }
}

Wenn Sie alle Dateien und Ordner löschen möchten, in denen Sie dieses Skript ablegen, rufen Sie es wie folgt auf

//get current working directory
$dir = getcwd();
unlinkr($dir);

Wenn Sie nur PHP-Dateien löschen möchten, rufen Sie es wie folgt auf

unlinkr($dir, "*.php");

Sie können auch einen anderen Pfad verwenden, um die Dateien zu löschen

unlinkr("/home/user/temp");

Dadurch werden alle Dateien im Verzeichnis home / user / temp gelöscht.


0

Ich benutze diesen Code ...

 function rmDirectory($dir) {
        foreach(glob($dir . '/*') as $file) {
            if(is_dir($file))
                rrmdir($file);
            else
                unlink($file);
        }
        rmdir($dir);
    }

oder dieses...

<?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); 
  } 
?>

Ist das rekursiv?
Martin Tournoij

0

Wenn Sie mit dem Ausführen der Tests fertig sind, entfernen Sie einfach # aus dem #unlink und #rmdir in der Klasse.

<?php 
class RMRFiles {

        function __construct(){
        }

    public function recScan( $mainDir, $allData = array() )
    {

    // hide files
    $hidefiles = array(
    ".",
    "..") ;

    //start reading directory
    $dirContent = scandir( $mainDir ) ;

        //cycle through
        foreach ( $dirContent as $key => $content )
        {
            $path = $mainDir . '/' . $content ;

            // if is readable / file
            if ( ! in_array( $content, $hidefiles ) )
            {
            if ( is_file( $path ) && is_readable( $path ) )
            {
            #delete files within directory
            #unlink($path);
            $allData['unlink'][] = $path ;
            }

            // if is readable / directory
            else
            if ( is_dir( $path ) && is_readable( $path ) )
            {
            /*recursive*/
            $allData = $this->recScan( $path, $allData ) ;

            #finally remove directory
            $allData['rmdir'][]=$path;
            #rmdir($path);
            }
            }
        }

    return $allData ;

    }

}

header("Content-Type: text/plain");

/* Get absolute path of the running script 
Ex : /home/user/public_html/   */
define('ABPATH', dirname(__file__) . '/'); 

/* The folder where we store cache files 
Ex: /home/user/public_html/var/cache   */
define('STOREDIR','var/cache'); 

$rmrf = new RMRFiles();
#here we delete folder content files & directories
print_r($rmrf->recScan(ABPATH.STOREDIR));
#finally delete scanned directory ? 
#rmdir(ABPATH.STOREDIR);

?>

0
<?php

/**
 * code by Nk (nk.have.a@gmail.com)
 */

class filesystem
{
    public static function remove($path)
    {
        return is_dir($path) ? rmdir($path) : unlink($path);
    }

    public static function normalizePath($path)
    {
        return $path.(is_dir($path) && !preg_match('@/$@', $path) ? '/' : '');      
    }

    public static function rscandir($dir, $sort = SCANDIR_SORT_ASCENDING)
    {
        $results = array();

        if(!is_dir($dir))
        return $results;

        $dir = self::normalizePath($dir);

        $objects = scandir($dir, $sort);

        foreach($objects as $object)
        if($object != '.' && $object != '..')
        {
            if(is_dir($dir.$object))
            $results = array_merge($results, self::rscandir($dir.$object, $sort));
            else
            array_push($results, $dir.$object);
        }

        array_push($results, $dir);

        return $results;
    }

    public static function rrmdir($dir)
    {
        $files = self::rscandir($dir);

        foreach($files as $file)
        self::remove($file);

        return !file_exists($dir);
    }
}

?>

cleanup.php:

<?php

/* include.. */

filesystem::rrmdir('/var/log');
filesystem::rrmdir('./cache');

?>

0

Es scheint, dass alle anderen Antworten davon ausgehen, dass der der Funktion zugewiesene Pfad immer ein Verzeichnis ist. Diese Variante entfernt Verzeichnisse sowie einzelne Dateien:

/**
 * Recursively delete a file or directory.  Use with care!
 *
 * @param string $path
 */
function recursiveRemove($path) {
    if (is_dir($path)) {
        foreach (scandir($path) as $entry) {
            if (!in_array($entry, ['.', '..'])) {
                recursiveRemove($path . DIRECTORY_SEPARATOR . $entry);
            }
        }
        rmdir($path);
    } else {
        unlink($path);
    }
}

0

DirectoryIterator und Rekursion korrekt verwenden:

function deleteFilesThenSelf($folder) {
    foreach(new DirectoryIterator($folder) as $f) {
        if($f->isDot()) continue; // skip . and ..
        if ($f->isFile()) {
            unlink($f->getPathname());
        } else if($f->isDir()) {
            deleteFilesThenSelf($f->getPathname());
        }
    }
    rmdir($folder);
}

-1

Ich habe diesen Code aus einigen StackOverflow-Diskussionen erstellt. Ich habe noch nicht in der Linux-Umgebung getestet. Es wird erstellt, um eine Datei oder ein Verzeichnis vollständig zu löschen:

function splRm(SplFileInfo $i)
{
    $path = $i->getRealPath();

    if ($i->isDir()) {
        echo 'D - ' . $path . '<br />';
        rmdir($path);
    } elseif($i->isFile()) {
        echo 'F - ' . $path . '<br />';
        unlink($path);
    }
}

function splRrm(SplFileInfo $j)
{
    $path = $j->getRealPath();

    if ($j->isDir()) {
        $rdi = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
        $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ($rii as $i) {
            splRm($i);
        }
    }
    splRm($j);

}

splRrm(new SplFileInfo(__DIR__.'/../dirOrFileName'));

-1
function rmdir_recursive( $dirname ) {

    /**
     * FilesystemIterator and SKIP_DOTS
     */

    if ( class_exists( 'FilesystemIterator' ) && defined( 'FilesystemIterator::SKIP_DOTS' ) ) {

        if ( !is_dir( $dirname ) ) {
            return false;
        }

        foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dirname, FilesystemIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ) as $path ) {
            $path->isDir() ? rmdir( $path->getPathname() ) : unlink( $path->getRealPath() );
        }

        return rmdir( $dirname );

    }

    /**
     * RecursiveDirectoryIterator and SKIP_DOTS
     */

    if ( class_exists( 'RecursiveDirectoryIterator' ) && defined( 'RecursiveDirectoryIterator::SKIP_DOTS' ) ) {

        if ( !is_dir( $dirname ) ) {
            return false;
        }

        foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dirname, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ) as $path ) {
            $path->isDir() ? rmdir( $path->getPathname() ) : unlink( $path->getRealPath() );
        }

        return rmdir( $dirname );

    }

    /**
     * RecursiveIteratorIterator and RecursiveDirectoryIterator
     */

    if ( class_exists( 'RecursiveIteratorIterator' ) && class_exists( 'RecursiveDirectoryIterator' ) ) {

        if ( !is_dir( $dirname ) ) {
            return false;
        }

        foreach( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $dirname ), RecursiveIteratorIterator::CHILD_FIRST ) as $path ) {
            if ( in_array( $path->getFilename(), array( '.', '..' ) ) ) {
                continue;
            }
            $path->isDir() ? rmdir( $path->getPathname() ) : unlink( $path->getRealPath() );
        }

        return rmdir( $dirname );

    }

    /**
     * Scandir Recursive
     */

    if ( !is_dir( $dirname ) ) {
        return false;
    }

    $objects = scandir( $dirname );

    foreach ( $objects as $object ) {
        if ( $object === '.' || $object === '..' ) {
            continue;
        }
        filetype( $dirname . DIRECTORY_SEPARATOR . $object ) === 'dir' ? rmdir_recursive( $dirname . DIRECTORY_SEPARATOR . $object ) : unlink( $dirname . DIRECTORY_SEPARATOR . $object );
    }

    reset( $objects );
    rmdir( $dirname );

    return !is_dir( $dirname );

}

-1

Modifizierte Variante der @ XzaR-Lösung. Leere Ordner werden entfernt, wenn alle Dateien aus ihnen gelöscht wurden, und es werden Ausnahmen ausgelöst, anstatt bei Fehlern false zurückzugeben.

function recursivelyRemoveDirectory($source, $removeOnlyChildren = true)
{
    if (empty($source) || file_exists($source) === false) {
        throw new Exception("File does not exist: '$source'");
    }

    if (is_file($source) || is_link($source)) {
        if (false === unlink($source)) {
            throw new Exception("Cannot delete file '$source'");
        }
    }

    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::CHILD_FIRST
    );

    foreach ($files as $fileInfo) {
        /** @var SplFileInfo $fileInfo */
        if ($fileInfo->isDir()) {
            if ($this->recursivelyRemoveDirectory($fileInfo->getRealPath()) === false) {
                throw new Exception("Failed to remove directory '{$fileInfo->getRealPath()}'");
            }
            if (false === rmdir($fileInfo->getRealPath())) {
                throw new Exception("Failed to remove empty directory '{$fileInfo->getRealPath()}'");
            }
        } else {
            if (unlink($fileInfo->getRealPath()) === false) {
                throw new Exception("Failed to remove file '{$fileInfo->getRealPath()}'");
            }
        }
    }

    if ($removeOnlyChildren === false) {
        if (false === rmdir($source)) {
            throw new Exception("Cannot remove directory '$source'");
        }
    }
}

-1
function deltree_cat($folder)
{
    if (is_dir($folder))
    {
             $handle = opendir($folder);
             while ($subfile = readdir($handle))
             {
                     if ($subfile == '.' or $subfile == '..') continue;
                     if (is_file($subfile)) unlink("{$folder}/{$subfile}");
                     else deltree_cat("{$folder}/{$subfile}");
             }
             closedir($handle);
             rmdir ($folder);
     }
     else
     {
        unlink($folder);
     }
}

1
Wenn Sie eine alte Frage beantworten, die bereits eine Reihe von Antworten enthält, einschließlich einer akzeptierten, müssen Sie eine Erklärung veröffentlichen, welchen Wert Ihre Antwort bietet, nicht nur Code. Nur-Code-Antworten sind im Allgemeinen verpönt, besonders aber in diesem Fall.
Jared Smith

Ich habe für diese Antwort gestimmt und die Antwort akzeptiert. Das ist nicht schlecht, von meiner Benchmark - Prüfung (ohne unlink, rmdir) die opendir+ readdirArbeit schneller , dass scandirund RecursiveDirectoryIteratores auch weniger Speicher als alle zu verwenden ist. Um Ordner zu entfernen, muss ich closedirzuerst daran festhalten. Dank dieser Antwort.
Vee
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.