¿Eliminar directorio con archivos?

246

Me pregunto, ¿cuál es la forma más fácil de eliminar un directorio con todos sus archivos?

Estoy usando rmdir(PATH . '/' . $value);para eliminar una carpeta, sin embargo, si hay archivos dentro de ella, simplemente no puedo eliminarla.

mate
fuente
2
Sí, respondió exactamente en esa pregunta.
timdev
Solo quiero tener en cuenta. Creé varios archivos y si durante el proceso recibo algún error, entonces necesito eliminar los archivos creados previamente. Cuando se crearon los archivos, se olvidó de usarlos fclose($create_file);y cuando se eliminaron, obtuve Warning: unlink(created_file.xml): Permission denied in.... Entonces, para evitar tales errores, debe cerrar los archivos creados.
Andris

Respuestas:

382

Hay al menos dos opciones disponibles en la actualidad.

  1. Antes de eliminar la carpeta, elimine todos sus archivos y carpetas (¡y esto significa recurrencia!). Aquí hay un ejemplo:

    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. Y si está utilizando 5.2+, puede usar un RecursiveIterator para hacerlo sin implementar la recursividad usted mismo:

    $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);
    
alcuadrado
fuente
11
Su segunda implementación es algo peligrosa: no comprueba los puntos ( .y ..) y elimina la ruta resuelta, no la real.
Alix Axel
99
pequeño complemento :-) glob () no es compatible con archivos como .htaccess. Utilicé la función para borrar directorios creados por KCFinder (complemento CKEditor) que genera tanto .htaccess como .thumbs (archivo + carpeta). En su lugar, utilicé la scandirfunción para obtener la lista de carpetas. Solo asegúrese de filtrar el '.' y '...' archivos de la lista de resultados.
Joshua - Pendo
25
DIRECTORY_SEPARATOR no es necesario cuando está creando rutas para enviar al sistema operativo. Windows también aceptará barras diagonales. Es principalmente útil para explode()trazar una ruta tomada del sistema operativo. alanhogan.com/tips/php/directory-separator-not-necessary
ReactiveRaven
55
Además de @Alix Axel, usar aquí [SplFileInfo :: getRealPath ()] ( php.net/manual/en/splfileinfo.getrealpath.php ) no es una buena idea. Este método expande todos los enlaces simbólicos, es decir, se eliminará un archivo real de algún lugar en lugar de un enlace simbólico del directorio de destino. Debería usar SplFileInfo :: getPathname () en su lugar.
Vijit
2
Estoy de acuerdo con @Vijit, use getPathname () en lugar de getRealPath (). Hace lo mismo sin eliminar más de lo que espera si se encuentran enlaces simbólicos.
JoeMoe1984
196

Generalmente uso esto para eliminar todos los archivos en una carpeta:

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

Y luego puedes hacer

rmdir($dirname);
usuario3033886
fuente
26
Esto no elimina carpetas de forma recursiva; solo funciona si la carpeta solo contiene archivos regulares, todos los cuales tienen extensiones de archivo.
mgnb
55
Si no se necesita recurrencia, esta es la mejor y más simple respuesta hasta ahora. ¡Gracias!
eisbehr
2
Para eliminar todos los archivos de una carpeta, no solo los que tienen extensiones, use glob de la siguiente manera: array_map('unlink', glob("$dirname/*"));Esto aún no le permite eliminar directorios anidados en la carpeta.
kremuwa
Tenga en cuenta que esto también eliminará los archivos de puntos (ocultos).
BadHorsie
85

¿Cuál es la forma más fácil de eliminar un directorio con todos sus archivos?

system("rm -rf ".escapeshellarg($dir));
Su sentido común
fuente
33
Espero que no hables en serio. ¿Qué sucede si $ dir es /
The Pixel Developer
108
@ Lo mismo que con cualquiera de los códigos anteriores. ¿No es así?
Su sentido común
77
Tenga en cuenta que, dependiendo de cómo $dirse genere / proporcione, es posible que deba realizar un preprocesamiento adicional para estar seguro y evitar errores. Por ejemplo, si $dirpudiera tener un espacio sin escape o punto y coma en él, entonces podría haber efectos secundarios indeseables. Este no es el caso con las respuestas que usan cosas como rmdir()porque manejará los caracteres especiales para usted.
Trott
55
Versión de Windows:system('rmdir '.escapeshellarg($path).' /s /q');
Cypher
2
@ThePixelDeveloper no debería preocuparse por eliminar /, esto solo funcionaría si lanzara el script en la línea de comando como root, porque en la web todo sucede como usuario apache
Ben
49

Función corta que hace el trabajo:

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

Lo uso en una clase de Utils como esta:

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

Con un gran poder viene una gran responsabilidad : cuando llama a esta función con un valor vacío, eliminará los archivos que comienzan en root ( /). Como medida de seguridad, puede verificar si la ruta está vacía:

function deleteDir($path) {
    if (empty($path)) { 
        return false;
    }
    return is_file($path) ?
            @unlink($path) :
            array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
}
Blaise
fuente
1
El estático no funciona porque $ this === NULL cuando llama a una función estática en una clase. Funcionaría si$this_func = array(__CLASS__, __FUNCTION__);
Matt Connolly
2
¿Alguien puede explicar la línea array_map($class_func, glob($path.'/*')) == @rmdir($path)? Supongo que está recurriendo a través de las subcarpetas, pero ¿qué hace la parte == @rmdir? ¿Cómo la <matriz de booleanos> == <booleano> devuelve la respuesta? ¿Comprueba si cada valor de retorno de la recursividad es el mismo que el booleano de la derecha?
arviman
2
Es un truco combinar dos declaraciones en una sola declaración. Esto se debe a que los operadores ternarios solo permiten una declaración por argumento. array_map(...)elimina todos los archivos dentro del directorio, @rmdir(...)elimina el directorio en sí.
Blaise
3
¡Ten cuidado! Esta función no comprueba si la ruta realmente existe. Si pasa un argumento vacío, la función comenzará a eliminar archivos a partir de la raíz. Agregue una comprobación de cordura a su ruta antes de ejecutar esta función.
Tatu Ulmanen
3
Algunas personas no vieron el comentario de Tatu y lo borraron recursivamente /, así que agregué una versión protegida a mi publicación.
Blaise
22

Como se ve en el comentario más votado en la página del manual de PHP sobre rmdir()(ver http://php.net/manual/es/function.rmdir.php ), la glob()función no devuelve archivos ocultos. scandir()se proporciona como una alternativa que resuelve ese problema.

El algoritmo descrito allí (que funcionó como un encanto en mi caso) es:

<?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); 
    } 
?>
Latorre alemán
fuente
¿puede explicar is_dir ("$ dir / $ file")? no se reunió con el parámetro "$ dir / $ file"
Igor L.
¿Qué quieres decir? Comprueba si la entrada encontrada en un directorio ( $file) es un directorio o un archivo. "$dir/$file"es el mismo que $dir . "/" . $file.
Alemán Latorre
Sinceramente, no sabía que puede concatenar variables como esta :) thx
Igor L.
18

Esta es una versión más corta que me funciona muy bien

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);
    }
}
Playnox
fuente
15

Puede usar el sistema de archivos de Symfony ( código ):

// composer require symfony/filesystem

use Symfony\Component\Filesystem\Filesystem;

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

Sin embargo, no pude eliminar algunas estructuras de directorio complejas con este método, así que primero debes probarlo para asegurarte de que funcione correctamente.


Podría eliminar dicha estructura de directorio utilizando una implementación específica de Windows:

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


Y solo por completar, aquí hay un viejo código mío:

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);
}
Gras Double
fuente
Muchas gracias. Me ahorras tiempo.
zarif khan
"No reinventes la rueda" . Gracias
Kamafeather
9

Aquí tiene una recursión agradable y simple para eliminar todos los archivos en el directorio de origen, incluido ese directorio:

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

}

La función se basa en la recursividad realizada para copiar el directorio. Puede encontrar esa función aquí: Copie todo el contenido de un directorio a otro usando php

Tommz
fuente
4

La mejor solución para mi

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

código:

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 RECUERDA!
¡no pase VALORES VACÍOS a ninguna función de eliminación de Directorio! (¡Respaldarlos siempre, de lo contrario, algún día podrías DESASTRAR!)

T.Todua
fuente
4

¿Qué hay de esto?

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);
}
adrian
fuente
4

La función Glob no devuelve los archivos ocultos, por lo tanto, scandir puede ser más útil cuando se trata de eliminar un árbol de forma recursiva.

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

Puedes probar lo siguiente:

/*
 * 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);
        }
    }
}
Bablu Ahmed
fuente
3

¡Prefiero esto porque todavía devuelve VERDADERO cuando tiene éxito y FALSO cuando falla, y también evita un error en el que una ruta vacía puede intentar eliminar todo de '/ *'!

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);
}
Matt Connolly
fuente
3

Quiero ampliar la respuesta de @alcuadrado con el comentario de @Vijit para manejar enlaces simbólicos. En primer lugar, use getRealPath (). Pero luego, si tiene enlaces simbólicos que son carpetas, fallará, ya que intentará llamar a rmdir en un enlace, por lo que necesita una comprobación adicional.

$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);
usuario701152
fuente
1
No tengo suficiente representante para comentar la respuesta directamente.
user701152
3

Usando DirectoryIterator un equivalente de una respuesta anterior ...

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);
}
Alan Trewartha
fuente
3

Esta funciona para mí:

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

¿Algo como esto?

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

Pequeña modificación del código de alcuadrado: globno veo archivos con nombre de puntos como, .htaccessasí que uso scandir y el script se elimina solo, verifique __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);
}
karma_police
fuente
2

Ejemplo para el servidor Linux: exec('rm -f -r ' . $cache_folder . '/*');

realmag777
fuente
Por lo general, me gusta agregar una comprobación de cordura en $ cache_folder antes de ejecutar rm -rf para evitar errores costosos
glifo
1

Eliminar todos los archivos en la carpeta
array_map('unlink', glob("$directory/*.*"));
Eliminar todo. * - Archivos en la carpeta (sin: "." Y "..")
array_map('unlink', array_diff(glob("$directory/.*),array("$directory/.","$directory/..")))
Ahora elimine la carpeta en sí
rmdir($directory)

PP2000
fuente
1

2 centavos para agregar a ESTA respuesta anterior, lo cual es genial, por cierto

Después de que su función global (o similar) haya escaneado / leído el directorio, agregue un condicional para verificar que la respuesta no esté vacía, o se invalid argument supplied for foreach()lanzará una advertencia. Entonces...

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

Mi función completa (como método de objeto):

    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()
Phil Meadows
fuente
1

Aquí está la solución que funciona perfectamente:

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;
}
Tarik
fuente
1

Podrías copiar los ayudantes de YII

$ directorio (cadena): se eliminará de forma recursiva.

$ opciones (matriz): para eliminar el directorio. Las opciones válidas son: traverseSymlinks: boolean, si los enlaces simbólicos a los directorios también deben atravesarse. El valor predeterminado es false, lo que significa que no se eliminará el contenido del directorio enlazado. Solo el enlace simbólico se eliminaría en ese caso predeterminado.

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);
    }
}
José Veríssimo
fuente
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);
  }
 }
?>

Haz probado el código obove de php.net

Trabaja para mi bien

Gaurang
fuente
0

Para ventanas:

system("rmdir ".escapeshellarg($path) . " /s /q");
Mi lo
fuente
0

Como la solución de Playnox, pero con el elegante DirectoryIterator incorporado:

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!');
  }
 }
Matthew Slyman
fuente
0

No recuerdo de dónde copié esta función, pero parece que no está en la lista y está funcionando para mí.

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);
    }
}
dav
fuente
0

Simple y fácil...

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

¿Qué hay de esto?

function Delete_Directory($Dir) 
{
  if(is_dir($Dir))
  {
      $files = glob( $Dir . '*', GLOB_MARK ); //GLOB_MARK adds a slash to directories returned

      foreach( $files as $file )
      {
          Delete_Directory( $file );      
      }
      if(file_exists($Dir))
      {
          rmdir($Dir);
      }
  } 
  elseif(is_file($Dir)) 
  {
     unlink( $Dir );  
  }
}

Referencia: https://paulund.co.uk/php-delete-directory-and-files-in-directory

Mohamad Hamouday
fuente
0

Si no está seguro, la ruta dada es un directorio o archivo, entonces puede usar esta función para eliminar la ruta

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;
        }
}
jay padaliya
fuente