Видалити каталог з файлами в ньому?


246

Цікаво, який найпростіший спосіб видалити каталог із усіма його файлами в ньому?

Я використовую rmdir(PATH . '/' . $value);для видалення папки, однак, якщо всередині неї є файли, я просто не можу її видалити.



2
Так, відповіли саме на це питання.
timdev

Просто хочу зазначити. Я створив декілька файлів, і якщо під час процесу з’явиться якась помилка, то потрібно видалити раніше створені файли. Коли створювали файли, забували користуватися, fclose($create_file);а при видаленні отримували Warning: unlink(created_file.xml): Permission denied in.... Тому, щоб уникнути подібних помилок, слід закрити створені файли.
Андріс

Відповіді:


382

На сьогоднішній день є щонайменше два варіанти.

  1. Перш ніж видалити папку, видаліть усі її файли та папки (а це означає рекурсію!). Ось приклад:

    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. І якщо ви використовуєте 5.2+, ви можете використовувати RecursiveIterator для цього, не застосовуючи рекурсії самостійно:

    $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
Ваша друга реалізація дещо небезпечна: вона не перевіряє крапки ( .і ..) і видаляє вирішений шлях, а не власне.
Алікс Аксель

9
невеликий додаток :-) glob () не підтримує файли, такі як .htaccess. Я використовував цю функцію для очищення каталогів, створених KCFinder (плагін CKEditor), який генерує і .htaccess, і .thumbs (файл + папка). Натомість я скористався scandirфункцією для отримання списку папок. Просто переконайтеся, що ви фільтруєте "." та ".." файли зі списку результатів.
Джошуа - Пендо

25
DIRECTORY_SEPARATOR не потрібен, коли ви будуєте шляхи для надсилання до ОС. Windows також прийме нахили вперед. Її в основному корисно використовувати для explode()шляху, взятого з ОС. alanhogan.com/tips/php/directory-separator-not-ne potrebno
ReactiveRaven

5
На додаток до @Alix Axel Використання тут [SplFileInfo :: getRealPath ()] ( php.net/manual/en/splfileinfo.getrealpath.php ) - не дуже гарна ідея. Цей метод розширює всі символьні посилання, тобто буде видалений реальний файл звідкись, а не символьне посилання з цільового каталогу. Слід використовувати замість SplFileInfo :: getPathname ().
Віджіт

2
Я згоден з @Vijit, використовуйте getPathname () замість getRealPath (). Це робиться те саме, не видаляючи більше того, що ви очікуєте, якщо знайдеться посилання.
JoeMoe1984

196

Я зазвичай використовую це для видалення всіх файлів у папці:

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

І тоді ви можете зробити

rmdir($dirname);

27
Це не видаляє папки рекурсивно; вона працює лише в тому випадку, якщо в папці є лише звичайні файли, всі вони мають розширення файлів.
mgnb

5
Якщо рекурсії не потрібні, це найкращий і найпростіший відповідь на даний момент. Дякую!
eisbehr

2
Щоб видалити всі файли з папки, не лише ті, що мають розширення, використовуйте глобус наступним чином: array_map('unlink', glob("$dirname/*"));Це все ще не дозволяє видаляти каталоги, вкладені в папку.
kremuwa

Зверніть увагу, що це також видалить крапки (приховані) файли.
BadHorsie

84

який найпростіший спосіб видалити каталог із усіма файлами в ньому?

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

33
Сподіваюся, ти несерйозний. Що станеться, якщо $ dir є /
The Pixel Developer

108
@Точно так само, як і з будь-яким із наведених вище кодів. Чи не так?
Твій здоровий глузд

7
Зауважте, що, залежно від способів $dirгенерації / надання, вам може знадобитися зробити додаткову попередню обробку, щоб бути безпечною та уникнути помилок. Наприклад, якщо в ньому $dirможе бути незміщений простір або напівкрапка, то можуть виникнути небажані побічні ефекти. Це не так у відповідях, які використовують такі речі, rmdir()тому що вони будуть обробляти спеціальні символи для вас.
Тротт

5
Версія для Windows:system('rmdir '.escapeshellarg($path).' /s /q');
Cypher

2
@ThePixelDeveloper ви не повинні турбуватися про видалення /, це спрацює лише в тому випадку, якщо ви запустите скрипт у командному рядку як корінь, тому що в Інтернеті все відбувається як користувач apache
Бен

49

Коротка функція, яка виконує роботу:

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

Я використовую його в класі Utils так:

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

З великою потужністю покладається велика відповідальність : Коли ви викликаєте цю функцію з порожнім значенням, вона видалить файли, що починаються з root ( /). В якості гарантії ви можете перевірити, чи шлях порожній:

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

1
Статичний не працює, оскільки $ this === NULL, коли ви викликаєте статичну функцію в класі. Це спрацювало б, якби$this_func = array(__CLASS__, __FUNCTION__);
Метт Конноллі,

2
Може хтось пояснить рядок array_map($class_func, glob($path.'/*')) == @rmdir($path)? Я думаю, що він повторюється через підпапки, але що робить == @rmdir? Як <масив booleans> == <boolean> повертає відповідь? Чи перевіряється, чи кожне повернене значення рекурсії збігається з булевим праворуч?
арвіман

2
Це хитрість об’єднати два твердження в одне твердження. Це тому, що потрійні оператори дозволяють лише одне твердження на аргумент. array_map(...)видаляє всі файли в каталозі, @rmdir(...)видаляє сам каталог.
Blaise

3
Будь обережний! Ця функція не перевіряє, чи дійсно існує шлях. Якщо ви передасте порожній аргумент, функція почне видаляти файли, починаючи з кореня! Додайте до свого шляху перевірку обґрунтованості, перш ніж запускати цю функцію.
Тату Ульманен

3
Деякі люди не бачили коментаря Тату і рекурсивно видаляли /, тому я додав до свого повідомлення захищену версію.
Блейз

22

Як видно з більшості проголосованих коментарів на сторінці керівництва PHP rmdir()(див. Http://php.net/manual/es/function.rmdir.php ), glob()функція не повертає приховані файли. scandir()надається як альтернатива, яка вирішує це питання.

Алгоритм, описаний там (який працював як шарм у моєму випадку):

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

можете пояснити, будь ласка, is_dir ("$ dir / $ file") - не зустрічався з параметром "$ dir / $ file"
Ігор Л.

Що ви маєте на увазі? Він перевіряє, чи є запис, знайдений у каталозі ( $file), каталогом або файлом. "$dir/$file"те саме, що $dir . "/" . $file.
Німецький Латорр

Я, чесно кажучи, не знав, що ви можете об'єднати такі змінні :) thx
Ігор Л.

18

Це коротша версія, мені чудово підходить

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

Ви можете використовувати файлову систему ( код ) Symfony :

// composer require symfony/filesystem

use Symfony\Component\Filesystem\Filesystem;

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

Однак я не міг видалити деякі складні структури каталогів цим методом, тому спочатку слід спробувати його, щоб переконатися, що він працює належним чином.


Я міг би видалити зазначену структуру каталогу за допомогою конкретної реалізації для Windows:

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


І просто для повноти ось мій старий код:

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

Велике спасибі. Ти економиш мій час.
заріф-хан

"Не винаходити колесо" . Дякую
Kamafeather

9

Тут є одна приємна і проста рекурсія для видалення всіх файлів у вихідному каталозі, включаючи цей каталог:

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

}

Функція заснована на рекурсії, зробленій для копіювання каталогу. Ви можете знайти цю функцію тут: Скопіюйте весь вміст каталогу в інший за допомогою php


4

Найкраще рішення для мене

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

код:

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 ПАМ’ЯТАЙТЕ!
не передайте ПОПУТНІ ЦІННОСТІ в будь-який каталог видалення функцій !!! (створюйте резервні копії завжди, інакше в один прекрасний день ви можете отримати СПРАВУ!)


4

Як що до цього:

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

Функція Glob не повертає приховані файли, тому скандір може бути кориснішим при спробі видалити дерево рекурсивно.

<?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

Ви можете спробувати наступним чином:

/*
 * 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

Я вважаю за краще це, тому що він все ще повертає ІСТИНА, коли це досягає успіху, і ЛІЖНЕ, коли він не працює, а також запобігає помилку, де порожній шлях може спробувати видалити все з '/ *' !!:

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

Я хочу розкрити відповідь @alcuadrado з коментарем @Vijit для обробки символьних посилань. По-перше, використовуйте getRealPath (). Але тоді, якщо у вас є будь-які посилання, які є папками, вона вийде з ладу, оскільки вона спробує викликати rmdir за посиланням - значить, вам потрібна додаткова перевірка.

$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
У мене недостатньо респондентів, щоб коментувати відповідь безпосередньо.
user701152

3

Використання DirectoryIterator еквівалент попередньої відповіді…

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

Цей для мене працює:

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

2

Щось на зразок цього?

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

2

Біт Літл модифікує код alcuadrado - globне бачити файли з назвою з таких точок, як .htaccessя використовую скандір і сценарій видаляє себе - перевірте __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

Приклад для сервера Linux: exec('rm -f -r ' . $cache_folder . '/*');


Зазвичай я люблю додавати перевірку здоровості на $ cache_folder перед запуском rm -rf, щоб уникнути дорогих помилок
гліф

1

Видалити всі файли в папці
array_map('unlink', glob("$directory/*.*"));
Видалити всі. * - Файли в папці (без: "." Та "..")
array_map('unlink', array_diff(glob("$directory/.*),array("$directory/.","$directory/..")))
Тепер видаліть саму папку
rmdir($directory)


1

2 центи, щоб додати цю ЦІЙ відповідь вище, що здорово

Після того, як ваша глобальна (або подібна) функція сканує / прочитає каталог, додайте умовний варіант, щоб перевірити, чи відповідь не порожня, invalid argument supplied for foreach()інакше буде видано попередження. Так...

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

Моя повна функція (як об'єктний метод):

    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

Ось рішення, яке працює ідеально:

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

Ви можете скопіювати помічників YII

$ каталог (рядок) - видаляється рекурсивно.

$ options (масив) - для видалення каталогу. Допустимими є такі варіанти: traverseSymlinks: булева, чи слід також переходити посилання на каталоги. За замовчуванням - це falseозначає, що вміст каталогів, що посилається на нього, не буде видалено. У цьому випадку за замовчуванням буде видалено лише символьне посилання.

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

Запропонуйте код коба з php.net

Працюй для мене чудово



0

Як і рішення Playnox, але з елегантним вбудованим 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

Я не пам’ятаю, звідки я скопіював цю функцію, але схоже, що її немає в списку і вона працює для мене

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

Просто і легко ...

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


0

Якщо ви не впевнені, заданий шлях - це каталог або файл, ви можете використовувати цю функцію для видалення шляху

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;
        }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.