PHP递归删除目录下所有文件但保留空文件夹,需用scandir()遍历并仅对is_file()为真的条目调用unlink(),严格跳过“.”和“..”,不递归处理子目录,避免误用rmdir()删除目录本身。

PHP递归删除目录下所有文件但保留空文件夹
直接用 scandir() 遍历 + unlink() 删除文件,跳过 . 和 .. 即可。关键不是“清空”,而是“不删目录本身”。很多人误用 rmdir() 导致整个目录被删掉,这是最常踩的坑。
示例逻辑:
function clearDir($dir) {
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . '/' . $item;
if (is_file($path)) {
unlink($path);
}
// 不处理子目录:即跳过递归,只删当前层文件
}
}
- 务必检查
is_file(),否则可能把子目录当文件尝试unlink(),触发警告 - 路径末尾不要硬加
/,用realpath()或dirname(__FILE__) . DIRECTORY_SEPARATOR更安全 - 权限不足时
unlink()会静默失败,建议加if (!unlink($path)) trigger_error("Failed to delete $path", E_USER_WARNING);
PHP彻底清空并删除整个目录(含子目录)
必须手动递归,PHP 没有内置的“强制清空+删目录”原子操作。exec('rm -rf') 看似简单,但在 Windows、禁用函数或容器环境里大概率失效,不可靠。
可靠做法是先递归删内容,再删目录本身:
立即学习“PHP免费学习笔记(深入)”;
function removeDir($dir) {
if (!is_dir($dir)) return;
$items = scandir($dir);
foreach ($items as $item) {
if ($item === '.' || $item === '..') continue;
$path = $dir . '/' . $item;
if (is_dir($path)) {
removeDir($path); // 先清空并删子目录
} else {
unlink($path);
}
}
return rmdir($dir); // 最后删自己
}
- 顺序不能反:必须先删完所有子项,
rmdir()才能成功,否则报错Directory not empty - 注意符号链接:默认
is_dir()对软链返回false,若需处理,请改用is_link()单独判断 - 大目录下大量小文件时,递归深度可能触发 PHP 的
max_execution_time,建议加set_time_limit(0)或分批处理
用 RecursiveDirectoryIterator 安全遍历删除
比手写 scandir() 更健壮,自动跳过 ./..,支持过滤、异常捕获,适合生产环境。
function clearDirSafely($dir) {
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($it as $file) {
if ($file->isFile()) {
unlink($file->getPathname());
} elseif ($file->isDir()) {
rmdir($file->getPathname());
}
}
}
-
CHILD_FIRST是关键:确保子项先于父目录处理,避免“目录非空”错误 -
FilesystemIterator::SKIP_DOTS比手动判断./..更可靠,尤其在某些文件系统下 - 遇到权限问题时,
RecursiveDirectoryIterator构造可能直接抛出UnexpectedValueException,需 try/catch 包裹
为什么不用 shell_exec('rm -rf') 或 system()
因为不可控。常见失效场景包括:safe_mode(已废弃但仍有遗留配置)、disable_functions 列表含 exec、Windows 无 rm 命令、Docker 容器里没装 coreutils、SELinux 限制、路径含空格或特殊字符导致命令截断。
- 即使
shell_exec()返回空字符串,也不代表成功——得检查返回码,而 PHP 默认不暴露它 -
escapeshellarg()能防注入,但解决不了跨平台和权限问题,属于“表面安全,实际脆弱” - 线上环境审计通常禁止执行 shell 命令,硬编码
rm -rf可能直接被安全扫描拦截
真正需要速度时,不是换 shell,而是优化 PHP 递归逻辑:加 opcache.enable_cli=1、用 array_values(scandir()) 避免键名干扰、或改用 glob($dir . '/*', GLOB_NOSORT) 提速 —— 但前提是确认不需要遍历隐藏文件。



















