用glob()批量匹配旧文件最轻量可控,仅匹配当前层级文件,需配合filemtime()判断时间,注意路径分隔符和权限问题。

PHP怎么用 glob() 批量匹配旧文件
直接用 glob() 是最轻量、最可控的方式,尤其适合按时间筛选(比如“7天前的 .log 文件”)。它不依赖递归遍历整个目录树,避免误删子目录里的新文件。
关键点在于通配符写法和时间判断逻辑:
-
glob()只匹配**当前层级**文件,不会自动进子目录——这反而是优势,防止误伤 - 必须配合
filemtime()或filectime()做时间判断,glob()本身不提供时间过滤 - 注意 Windows 下路径分隔符问题:统一用
/或DIRECTORY_SEPARATOR,别硬写\
示例:删除 /var/log/app/ 下 30 天前的 .tmp 文件
$path = '/var/log/app/*.tmp';
$files = glob($path);
$threshold = time() - 30 * 86400;
<p>foreach ($files as $file) {
if (is_file($file) && filemtime($file) < $threshold) {
unlink($file);
}
}为什么不用 RecursiveDirectoryIterator 清理旧文件
它能递归进所有子目录,听起来很全,但实际用于“清理旧文件”反而容易出错——你通常只希望清理日志、缓存这类特定位置的旧文件,而不是把 vendor/ 或 config/ 里任何“老文件”都删掉。
立即学习“PHP免费学习笔记(深入)”;
常见踩坑点:
- 没加
isFile()判断,结果把整个子目录当文件尝试unlink(),报Warning: unlink(): Is a directory - 没跳过
.和..,某些 PHP 版本下会触发Uncaught RuntimeException - 性能差:即使只想删根目录下的旧文件,它仍会扫描全部子目录,IO 开销明显上升
真要用它,至少包一层过滤:
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/path', FilesystemIterator::SKIP_DOTS)
);
foreach ($iter as $file) {
if ($file->isFile() && $file->getMTime() < $threshold) {
unlink($file->getPathname());
}
}
unlink() 删除失败的三个高频原因
不是代码写错,而是环境或权限细节被忽略。报错时先看这三项:
- PHP 进程用户(如
www-data或nginx)对目标文件**没有写权限**,unlink()直接静默失败(注意:不抛异常,只返回false) - 文件被其他进程占用(比如日志正在被
tail -f或服务写入),Linux 下可删,但 Windows 会报Permission denied - 路径含中文或特殊字符,且 PHP 脚本编码与文件系统编码不一致(常见于 macOS 或老旧 CentOS),
unlink()找不到文件
安全做法是加判断:
if (is_writable($file) && unlink($file)) {
// 成功
} else {
error_log("Failed to delete: " . $file);
}定时清理建议用 shell 脚本而非 PHP
PHP 适合响应式清理(比如用户上传后自动删临时文件),但定期扫旧日志这种任务,交给 find 更稳、更省资源:
-
find /var/log/app -name "*.log" -mtime +7 -delete比 PHP 脚本快 3–5 倍,且不占 PHP-FPM 进程 - crontab 控制频率清晰(
0 2 * * *每天两点执行),不用在 PHP 里维护时间调度逻辑 - 避免 PHP 脚本因超时(
max_execution_time)或内存限制中断,导致部分文件漏删
如果非要用 PHP 启动定时任务,至少用 pcntl_fork() 或队列脱钩,别让 HTTP 请求卡住清理过程。
真正麻烦的是跨平台时间精度差异和 NFS 挂载点上的 mtime 不可靠——这些细节一旦出问题,删着删着就只剩空目录了。











