删除包含文件的目录?

310

我在想,删除一个包含所有文件的目录最简单的方法是什么?

我使用 rmdir(PATH . '/' . $value); 删除文件夹,但是,如果里面有文件,我就无法删除它。


2
是的,已经确切地回答了那个问题。 - timdev
只是想提醒一下。我创建了多个文件,如果在过程中出现错误,则需要删除先前创建的文件。在创建文件时,忘记使用 fclose($create_file);,在删除时出现了 Warning: unlink(created_file.xml): Permission denied in...。因此,为避免此类错误,必须关闭已创建的文件。 - Andris
34个回答

444

现在至少有两个选项可供选择。

  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来完成它,无需自己实现递归:

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

16
你的第二个实现方式有些危险:它没有检查句点(...),并且删除的是解析后的路径,而不是实际路径。 - Alix Axel
10
补充一下 :-) glob() 不支持像 .htaccess 这样的文件。我使用了这个函数清空由 KCFinder(CKEditor 插件)生成的目录,它会生成 .htaccess 和 .thumbs(文件 + 文件夹)。相反,我使用 scandir 函数获取文件夹列表。只需确保从结果列表中过滤掉 '.' 和 '..' 文件即可。 - Joshua - Pendo
28
在构建用于发送到操作系统的路径时,DIRECTORY_SEPARATOR是不必要的。Windows也接受正斜杠。它主要用于explode()从操作系统获取路径时使用。http://alanhogan.com/tips/php/directory-separator-not-necessary - ReactiveRaven
8
除了@Alix Axel之外,使用[SplFileInfo::getRealPath()](http://php.net/manual/en/splfileinfo.getrealpath.php)不是一个好主意。这个方法会展开所有符号链接,也就是说,会删除源目录中的真实文件而不是目标目录中的符号链接。你应该使用SplFileInfo::getPathname()。 - Vijit
4
我同意@Vijit的观点,使用getPathname()而不是getRealPath()。这样做可以在找到符号链接时避免删除超出预期的内容,但仍能达到相同的效果。 - JoeMoe1984
显示剩余14条评论

266

我通常使用以下方法来删除文件夹中的所有文件:

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

然后你可以做

rmdir($dirname);

41
这不会递归地删除文件夹;仅在文件夹中仅包含具有文件扩展名的常规文件时才有效。 - mgnb
13
如果不需要递归,这是目前为止最好和最简单的答案。谢谢! - eisbehr
7
为了从一个文件夹中删除所有文件,而不仅是带有扩展名的文件,可以按以下方式使用 glob: array_map('unlink', glob("$dirname/*")); 这仍然不能删除嵌套在该文件夹内的子目录。 - kremuwa
1
请注意,这也会删除点文件(隐藏文件)。 - BadHorsie
要递归删除文件夹,只需执行array_map("unlink", glob("$dirname/*")); array_map("rmdir", glob("$dirname/*")); rmdir($dirname); - Madeorsk

107

最简单的删除带有所有文件的目录的方法是什么?

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

59
我希望你不是认真的。如果 $dir 是 /,会发生什么? - The Pixel Developer
146
与上述任何代码完全相同,是吗? - Your Common Sense
12
请注意,根据 $dir 是如何生成/提供的,您可能需要进行一些额外的预处理以确保安全并避免错误。例如,如果 $dir 可能包含未转义的空格或分号,则可能会产生不良的副作用。对于使用 rmdir() 等功能的答案,这种情况并不适用,因为它会自动处理特殊字符。 - Trott
8
Windows 版本:system('rmdir '.escapeshellarg($path).' /s /q'); 翻译:在 Windows 系统中,此代码的作用是删除指定路径的文件夹及其内容,同时不提示确认信息。 - Cypher
7
如果你的 PHP 脚本可以访问并删除根目录下的文件或文件夹,那么无论如何都存在严重问题。 - Márton Tamás
显示剩余10条评论

56

简短的函数可以完成任务:

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

伟大的权力伴随着巨大的责任:当您使用一个空值调用此函数时,它将删除以根目录(/)开头的文件。为了保险起见,您可以检查路径是否为空:

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 部分是干什么的? <array of booleans> == <boolean> 是如何返回答案的?它是否检查递归的每个返回值是否与右侧的布尔值相同? - arviman
2
将两个语句合并成一个语句是一种技巧。这是因为三元运算符只允许每个参数有一个语句。array_map(...)会删除目录中的所有文件,而@rmdir(...)则会删除目录本身。 - Blaise
3
注意!此函数不会检查路径是否真实存在。如果传入空参数,该函数将从根目录开始删除文件!在运行此函数之前,请对路径进行合理性检查。 - Tatu Ulmanen
3
有些人没有看到Tatu的评论并递归删除了/,因此我在我的帖子中添加了一个受保护的版本。 - Blaise
显示剩余2条评论

39

如在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" 参数相遇的情况吗? - Igor L.
什么意思?它检查目录中找到的条目($file)是目录还是文件。"$dir/$file"$dir . "/" . $file相同。 - German Latorre
我真的不知道你可以像这样连接变量 :) 谢谢 - Igor L.
我在这个例子中遇到的唯一挑战是它不能很好地处理目录中的符号链接。要解决这个问题,将is_dir检查更改为(is_dir("$dir/$file") && !is_link("$dir/$file")) - General Redneck

24

你可以使用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

19

这个版本对我来说很好用。

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

您可以尝试以下操作:

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

1
这个函数的简洁美丽且工作得非常出色,除了在点文件上。 - Overcomer

13
我不敢相信这个问题有30多个答案。使用PHP递归删除文件夹可能需要几分钟,具体取决于目录深度和其中文件的数量!你只需要一行代码就可以做到这一点... shell_exec("rm -rf " . $dir); 如果你担心误删整个文件系统,请确保你的$dir路径是你想要的。绝对不能允许用户输入能直接删除文件的内容,必须先对输入进行严格的验证。这是编码实践的基本要求。

我正在清理 /tmp 文件夹,所以 shell_exec("rm -rf " . sys_get_temp_dir() . "/{$dir}"); 很容易、直接和非常快速。 - Francisco Luz
2
这个!但我会执行 shell_exec("rm -rf '$dir'"); - Eli Algranti
我正在使用cron作业来删除前一天上传的安全摄像头图像。显然,如上所述,请不要让用户访问此内容,并确保您正确设置了$dir! - drooh
不是每个人都可以访问 shell_exec(),但如果您可以的话,这将是一种可行的方法。 - Kerry Johnson

12

这个对我有效:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接