删除目录中的文件吗?


245

我想知道,删除其中包含所有文件的目录的最简单方法是什么?

我正在rmdir(PATH . '/' . $value);删除一个文件夹,但是,如果其中有文件,我将无法删除。



2
是的,完全回答了这个问题。
timdev

只是要注意。我创建了多个文件,如果在此过程中出现一些错误,则需要删除以前创建的文件。创建文件时忘记使用fclose($create_file);,删除文件时得到Warning: unlink(created_file.xml): Permission denied in...。因此,为避免此类错误,必须关闭创建的文件。
Andris

Answers:


380

如今至少有两种选择。

  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
第二种实现有些危险:它不检查点(...),并且删除解析的路径,而不是实际的路径。
Alix Axel

9
小插件:-) glob()不支持.htaccess之类的文件。我使用该功能清除了由KCFinder(CKEditor插件)创建的目录,该目录同时生成.htaccess和.thumbs(文件+文件夹)。相反,我使用该scandir函数来获取文件夹列表。只要确保您过滤了“。” 和结果列表中的“ ..”文件。
约书亚-Pendo 2012年

25
在构建要发送 os的路径时,不需要DIRECTORY_SEPARATOR 。Windows也将接受正斜杠。它主要用于 OS中explode()获取路径。alanhogan.com/tips/php/directory-separator-not-necessary
ReactiveRaven

5
除了@Alix Axel之外,在此处使用[SplFileInfo :: getRealPath()](php.net/manual/en/splfileinfo.getrealpath.php)并不是一个好主意。此方法扩展所有符号链接,这意味着将从某个位置删除一个真实文件,而不是从目标目录中删除一个符号链接。您应该改用SplFileInfo :: getPathname()。
维吉特

2
我同意@Vijit,使用getPathname()而不是getRealPath()。如果找到符号链接,它所做的相同的事情不会删除比您期望的更多的内容。
JoeMoe1984 '17

196

我通常使用它来删除文件夹中的所有文件:

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

然后你可以做

rmdir($dirname);

26
这不会递归删除文件夹。仅当文件夹中只有常规文件且所有文件都具有文件扩展名时,它才起作用。
mgnb 2015年

5
如果不需要递归,那么这是迄今为止最好和最简单的答案。谢谢!
eisbehr

2
为了从文件夹中删除所有文件,不仅是带有扩展名的文件,请按以下方式使用glob:array_map('unlink', glob("$dirname/*"));这仍然不允许您删除嵌套在文件夹中的目录。
kremuwa

请注意,这也会删除点(隐藏)文件。
BadHorsie19年

84

删除其中包含所有文件的目录的最简单方法是什么?

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

33
我希望你不认真。如果$ dir是/
像素开发人员,2010年

108
@与上面的任何代码完全相同。是不是
常识

7
请注意,根据$dir生成/提供的方式,您可能需要进行一些其他预处理,以确保安全并避免错误。例如,如果其中$dir可能有未转义的空间或分号,则可能会有不良的副作用。使用诸如此类之类的答案的情况并非如此,rmdir()因为它将为您处理特殊字符。
Trott 2012年

5
Windows版本:system('rmdir '.escapeshellarg($path).' /s /q');
Cypher

2
@ThePixelDeveloper,您不必担心删除/,仅当您以root用户身份在命令行中脚本时才有效,因为在Web中,一切都以apache用户的身份发生
Ben

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__);
Matt Connolly

2
有人可以解释这句话array_map($class_func, glob($path.'/*')) == @rmdir($path)吗?我猜他正在遍历子文件夹,但是== @rmdir部分有什么作用?<布尔数组> == <布尔>如何返回答案?它是否检查递归的每个返回值是否与右边的布尔值相同?
arviman 2013年

2
将两个语句合并为一个语句是一个技巧。这是因为三元运算符每个参数只允许一个语句。array_map(...)删除目录中的所有文件,@rmdir(...)删除目录本身。
布莱斯

3
小心!此功能不检查路径是否确实存在。如果传递一个空参数,该函数将开始从根目录开始删除文件!在运行此功能之前,请先对路径进行完整性检查。
塔图·乌尔曼嫩

3
有些人没有看到Tatu的评论并递归删除了/,所以我在自己的帖子中添加了受保护的版本。
Blaise 2014年

22

正如有关PHP手册页rmdir()(请参阅http://php.net/manual/es/function.rmdir.php)上大多数投票表决的评论所示,glob()function不返回隐藏文件。 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 / $文件”) -没有用“$ DIR / $文件”参数满足
伊戈尔L.

你什么意思?它检查在目录($file)中找到的条目是目录还是文件。 "$dir/$file"与相同$dir . "/" . $file
德国拉托雷

老实说,我不知道您可以连接这样的变量:) thx
Igor L.15

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

非常感谢。您可以节省我的时间。
zarif khan

“不要重新发明轮子”。谢谢
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函数不会返回隐藏文件,因此在尝试递归删除树时,scandir可能会更有用。

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

我之所以这样,是因为它成功时仍然返回TRUE,失败时仍然返回FALSE,并且还防止了一个空路径可能尝试从'/ *'中删除所有内容的错误!

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

我想用@Vijit的注释来扩展@alcuadrado的答案,以处理符号链接。首先,使用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因此我使用scandir并且脚本删除了自身-检查__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 . '/*');


我通常喜欢在运行rm -rf之前在$ cache_folder上添加健全性检查,以避免代价高昂的错误
字形

1

删除文件夹中的所有文件删除文件夹中的
array_map('unlink', glob("$directory/*.*"));
所有。*-文件(不带“。”和“ ..”)
array_map('unlink', array_diff(glob("$directory/.*),array("$directory/.","$directory/..")))
现在删除文件夹本身
rmdir($directory)


1

2美分加到这个答案上述,这是很好的BTW

在您的glob(或类似功能)函数扫描/读取目录后,添加条件以检查响应是否为空,否则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助手

$ directory(字符串)-递归删除。

$ 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的Obove代码

为我工作好


0

对于Windows:

system("rmdir ".escapeshellarg($path) . " /s /q");

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;
        }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.