如何在 PHP 中递归删除目录及其全部内容(文件 + 子目录)? [复制]

Posted

技术标签:

【中文标题】如何在 PHP 中递归删除目录及其全部内容(文件 + 子目录)? [复制]【英文标题】:How do I recursively delete a directory and its entire contents (files + sub dirs) in PHP? [duplicate] 【发布时间】:2011-03-21 06:27:15 【问题描述】:

如何在 php 中删除目录及其全部内容(文件和子目录)?

【问题讨论】:

这是一个很好的方法和解决方案:***.com/a/15111679/2377343 【参考方案1】:

rmdir 手册页中的用户贡献部分包含一个不错的实现:

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

【讨论】:

@The Pixel Developer - 我添加了 an answer 来表明这一点。 查看有人为相同问题提供的解决方案:glob 似乎工作得更好:***.com/questions/11267086/… 这会为每个递归目录调用两次is_dir。如果参数是符号链接,它也会跟随它而不是删除符号链接,这可能是也可能不是你想要的。无论如何,这不是rm -rf 所做的。【参考方案2】:

以The Pixel Developer's comment 为基础,使用 SPL 的 sn-p 可能如下所示:

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

注意:它会进行no健全性检查,并使用 PHP 5.3.0 中的 FilesystemIterator 引入的 SKIP_DOTS 标志。当然,$todo 可以是if/else。重要的一点是CHILD_FIRST 用于在其父级(文件夹)之前先遍历子级(文件)。

【讨论】:

SKIP_DOTS 仅在 PHP 5.3 中引入?你在哪里看到的? 谢谢。另外:您不应该使用getPathname() 方法而不是getRealPath() 此解决方案运行良好,但它会删除所有内容...除了目录(无论是否为空)。脚本末尾应该有一个rmdir($dir) Here is the same function 已解包,被文档阻止,并与rmdir()unlink() 保持一致,例如使用E_WARNING 中止并返回truefalse 表示成功。 @dbf 不会,FilesystemIterator 不是递归迭代器。【参考方案3】:

删除路径中的所有文件和文件夹。

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

【讨论】:

rm -rf / == recurseRmdir('/') :) 请注意,这不是符号链接安全的!您需要在 is_dir 之后进行完整性检查以检查它是否为 !is_link,否则您可以符号链接到外部文件夹,然后将其删除,这可能被视为安全漏洞。所以你应该把is_dir("$dir/$file")改成is_dir("$dir/$file") && !is_link("$dir/$file")【参考方案4】:

对于 *nix,您可以将 shell_exec 用于 rm -RDEL /S folder_name 对于 Windows。

【讨论】:

Windows 版DEL /S folder_name 怎么样 @Gordon RMDIR /S /Q folder_name 对我有用 @WiR3D 只要 exec 命令不包含用户输入,就应该没问题。例如:exec('rm -rf ' . __DIR__ . '/output/*.log');【参考方案5】:

这里有另一个线程有更多示例: A recursive remove directory function for PHP?

如果您使用的是 Yii,那么您可以将其留给框架:

CFileHelper::removeDirectory($my_directory);

【讨论】:

【参考方案6】:
<?php

use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

# http://***.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;

【讨论】:

相当复杂的建议 ;-) @Philipp 是的,我猜。好吧,我用gist.github.com/mindplay-dk/a4aad91f5a4f1283a5e2 做了一个叉子,因为我没有让它工作,所以我只是想我也可以分享它。 有问题。删除所有文件后,它不会删除空文件夹。在下面发布稍作修改的版本作为答案。 @Vladislav Rastrusny 真的吗?这个对我有用。也许你有一个只读文件夹。【参考方案7】:

正确使用 DirectoryIterator 和递归:

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

【讨论】:

【参考方案8】:

似乎所有其他答案都假定给函数的路径始终是一个目录。此变体可用于删除目录以及单个文件:

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

编辑:如果你很挑剔(你应该很挑剔),你可能需要添加代码来检查 scandir()rmdir()unlink() 返回错误,如果是则抛出异常。

【讨论】:

【参考方案9】:

“简单”的代码,10 岁的孩子都能看懂:

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

请注意,我所做的只是扩展/简化并修复(不适用于非空目录)解决方案: In PHP how do I recursively remove all folders that aren't empty?

【讨论】:

【参考方案10】:

增强的@Artefacto 的解决方案 - 纠正错别字和简化代码,同时适用于空 && 非空目录。

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

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

这样的?

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

【讨论】:

我无法解释原因,但这对我不起作用。它一直试图删除一个不为空的文件夹。上面的第二个答案效果很好。 @buggy3 你指的是哪个具体的代码?该链接仅链接到此问题页面。【参考方案13】:

glob() 函数的示例。它将递归删除所有文件和文件夹,包括以点开头的文件。

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

【讨论】:

我和system('rm -fr folder')一起去了【参考方案14】:

unlinkr 函数通过确保它不会删除脚本本身来递归地删除给定路径中的所有文件夹和文件。

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

如果要删除放置此脚本的所有文件和文件夹,请按以下方式调用它

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

如果您只想删除 php 文件,请按以下方式调用它

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

您也可以使用任何其他路径来删除文件

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

这将删除 home/user/temp 目录中的所有文件。

【讨论】:

【参考方案15】:

我用这个代码...

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

或者这个……

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

【讨论】:

这是递归的吗?【参考方案16】:

完成运行测试后,只需从类中的 #unlink#rmdir删除#

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

?>

【讨论】:

【参考方案17】:
<?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');

?>

【讨论】:

【参考方案18】:

我刚刚从一些 *** 讨论中制作了这段代码。我还没有在 Linux 环境下进行测试。它是为了完全删除文件或目录而制作的:

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

【讨论】:

【参考方案19】:
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 );


【讨论】:

【参考方案20】:

@XzaR 解决方案的修改变体。它确实会删除空文件夹,当所有文件都从其中删除时,它会抛出异常而不是在错误时返回 false。

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

【讨论】:

【参考方案21】:
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);
     

【讨论】:

如果您正在回答一个已经有多个答案(包括已接受的答案)的旧问题,您需要发布说明,说明您的答案增加了哪些价值,而不仅仅是代码。仅代码的答案通常不受欢迎,尤其是这种情况。 我投票赞成这个答案并接受了答案。这还不错,从我的基准检查(没有unlinkrmdir)来看,opendir + readdir 的工作速度比scandirRecursiveDirectoryIterator 更快,它也比所有使用的内存都少。要删除文件夹,我必须首先closedir,我被困在了这里。感谢这个答案。

以上是关于如何在 PHP 中递归删除目录及其全部内容(文件 + 子目录)? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

java FTPClient如何删除远程服务器端的文件夹及其子文件夹及其内容!

递归删除目录及其内容

Google Cloud Storage:如何在 Python 中(递归)删除文件夹

PHP的递归删除目录功能?

PHP获取目录中的全部内容RecursiveDirectoryIterator

JavaSE 文件递归之删除&amp;获取文件夹文件夹中全部的以.jpg的文件的绝对路径