Làm cách nào để xóa đệ quy một thư mục và toàn bộ nội dung của nó (tệp + thư mục con) trong PHP?


Câu trả lời:


207

Phần do người dùng đóng góp trong trang hướng dẫn rmdirchứa một triển khai hợp lý:

 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
@ Nhà phát triển Pixel - Tôi đã thêm một câu trả lời cho thấy điều đó.
chào mừng

2
kiểm tra giải pháp mà ai đó đưa ra cho tôi cho cùng một câu hỏi: global dường như hoạt động tốt hơn: stackoverflow.com/questions/11267086/ợi
NoodleOfDeath

Điều này gọi is_dirhai lần cho mỗi thư mục đệ quy. Nếu đối số là một liên kết tượng trưng, ​​nó cũng đi theo nó thay vì xóa liên kết tượng trưng, ​​có thể hoặc không thể là những gì bạn muốn. Trong mọi trường hợp, đó không phải là những gì rm -rf.
Vladimir Panteleev

116

Dựa trên nhận xét của Nhà phát triển Pixel , đoạn trích sử dụng SPL có thể trông như sau:

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

Lưu ý: Nó không kiểm tra sự tỉnh táo và làm cho việc sử dụng SKIP_DOTS cờ được giới thiệu với các FilesystemIterator trong PHP 5.3.0. Tất nhiên, $todocó thể là một if/ else. Điểm quan trọng là CHILD_FIRSTđược sử dụng để lặp lại trên các tệp con trước các tệp (thư mục) của chúng.


SKIP_DOTSchỉ được giới thiệu trong PHP 5.3? Bạn đã thấy điều đó ở đâu?
Alix Axel

Cảm ơn bạn. Ngoài ra: bạn không nên sử dụng getPathname()phương pháp thay vì getRealPath()?
Alix Axel

3
Giải pháp này hoạt động tốt, tuy nhiên nó xóa mọi thứ ... ngoại trừ thư mục (dù trống hay không). Nên có một rmdir($dir)phần cuối của kịch bản.
nguyệt quế

3
Dưới đây là các chức năng tương tự tháo, doc-chặn, và làm cho phù hợp với rmdir()unlink(), ví dụ như hủy bỏ với E_WARNINGvà lợi nhuận truehoặc falsechỉ thành công.
mindplay.dk

2
@dbf không nó sẽ không, nó không phải FilesystemIteratorlà một trình lặp đệ quy.
chào mừng

17

Xóa tất cả các tệp và thư mục trong đường dẫn.

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
Xin lưu ý rằng đây không phải là symlink an toàn! Bạn cần kiểm tra độ tỉnh táo sau is_dir để kiểm tra xem đó là is_link, vì nếu không, bạn có thể liên kết với một thư mục bên ngoài sau đó bị xóa và đây có thể được coi là lỗ hổng bảo mật. Vì vậy, bạn nên đổi is_dir("$dir/$file")thànhis_dir("$dir/$file") && !is_link("$dir/$file")
Kira M. Backes

13

Đối với * nix, bạn có thể sử dụng shell_execcho rm -Rhoặc DEL /S folder_namecho Windows.


2
Còn về DEL /S folder_nameWindows
ankitjaininfo

@Gordon RMDIR /S /Q folder_namelà những gì làm việc cho tôi
Brian Leishman

2
@ WiR3D miễn là lệnh exec không chứa đầu vào của người dùng, bạn sẽ ổn. Vd: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;
}

Đề xuất khá phức tạp ;-)
Philipp

@Philipp vâng tôi đoán thế. Vâng, tôi đã tạo một ngã ba từ gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2 vì tôi không làm cho nó hoạt động nên tôi chỉ nghĩ rằng tôi cũng có thể chia sẻ nó.
XzaR

Có một vấn đề. Nó không xóa các thư mục trống sau khi tất cả các tệp bị xóa. Đăng phiên bản sửa đổi một chút như một câu trả lời dưới đây.
Vladislav Rastrusny

@Vladislav Rastrusny thực sự? Nó làm việc cho tôi. Có lẽ bạn đã có một thư mục chỉ đọc hoặc một cái gì đó.
XzaR


1

Mã 'đơn giản' hoạt động và có thể được đọc bởi một đứa trẻ mười tuổi:

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

Xin lưu ý rằng tất cả những gì tôi đã làm là mở rộng / đơn giản hóa và sửa lỗi (không hoạt động đối với thư mục không trống) giải pháp ở đây: Trong PHP làm cách nào để tôi loại bỏ đệ quy tất cả các thư mục không trống?


1

Giải pháp nâng cao của @Artefacto - đã sửa lỗi chính tả và mã đơn giản hóa, hoạt động cho cả hai - thư mục trống && không trống.

  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

Giải pháp làm việc 100%

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

Một cái gì đó như thế này?

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

Tôi không thể giải thích tại sao nhưng điều đó không làm việc cho tôi. Nó tiếp tục cố gắng xóa một thư mục không trống. Câu trả lời thứ hai ở trên hoạt động tốt.
nguyệt quế

1
@ buggy3 Bạn đang đề cập đến mã cụ thể nào? Các liên kết chỉ đơn giản là liên kết đến trang câu hỏi này.
cgogolin

0

Ví dụ với hàm global () . Nó sẽ xóa tất cả các tệp và thư mục theo cách đệ quy, bao gồm các tệp bắt đầu bằng dấu chấm.

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

Tôi đã đi vớisystem('rm -fr folder')
Itay Moav -Malimovka

0

Hàm unlinkr xóa đệ quy tất cả các thư mục và tệp trong đường dẫn đã cho bằng cách đảm bảo rằng nó không xóa chính tập lệnh.

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

Nếu bạn muốn xóa tất cả các tệp và thư mục nơi bạn đặt tập lệnh này, hãy gọi nó như sau

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

Nếu bạn chỉ muốn xóa các tập tin php thì hãy gọi nó như sau

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

bạn cũng có thể sử dụng bất kỳ đường dẫn nào khác để xóa các tập tin

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

Điều này sẽ xóa tất cả các tập tin trong thư mục home / user / temp.


0

Tôi sử dụng mã này ...

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

hoặc cái này ...

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

Đây có phải là đệ quy?
Martin Tournoij

0

Khi bạn hoàn thành việc chạy thử, chỉ cần xóa # khỏi #unlink#rmdir trong lớp.

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

?>

dọn dẹp.php:

<?php

/* include.. */

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

?>

0

Dường như tất cả các câu trả lời khác đều cho rằng đường dẫn được cung cấp cho hàm luôn là một thư mục. Biến thể này hoạt động để loại bỏ các thư mục cũng như các tệp đơn lẻ:

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

Sử dụng DirectoryIterator và đệ quy chính xác:

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

Tôi vừa tạo mã này, từ một số cuộc thảo luận về StackOverflow. Tôi chưa thử nghiệm trên môi trường Linux. Nó được thực hiện để xóa một tập tin hoặc một thư mục, hoàn toàn:

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

Biến thể sửa đổi của giải pháp @XzaR. Nó xóa các thư mục trống, khi tất cả các tệp bị xóa khỏi chúng và nó ném ra các ngoại lệ thay vì trả về lỗi sai.

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
Nếu bạn đang trả lời một câu hỏi cũ đã có một số câu trả lời bao gồm một câu trả lời được chấp nhận, bạn cần đăng một lời giải thích về giá trị mà câu trả lời của bạn thêm vào, không chỉ là mã. Các câu trả lời chỉ dành cho mã được tán thành nói chung, nhưng đặc biệt là trường hợp này.
Jared Smith

Tôi đã bỏ phiếu cho câu trả lời này và chấp nhận câu trả lời. Đây không phải là xấu, từ kiểm tra benchmark của tôi (không unlink, rmdir) các opendir+ readdircông việc nhanh hơn scandirRecursiveDirectoryIteratornó cũng được sử dụng ít bộ nhớ hơn tất cả. Để xóa thư mục tôi phải closedirđầu tiên, tôi đã bị mắc kẹt ở đây. Nhờ câu trả lời này.
vee
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.