以下是一個安全的 PHP 腳本,用於掃描並刪除當前工作目錄下的所有文件和文件夾(不包括腳本自身,除非特別說明)。腳本包含交互確認,避免誤操作。
<?php
/**
* 遞歸刪除目錄下的所有內容(不包括當前目錄本身)
*
* @param string $dir 要清理的目錄路徑
* @param string|null $excludePath 需要排除的文件/目錄的絕對路徑(例如當前腳本自身)
* @return bool 是否成功完成(部分失敗也會繼續,返回 false 表示有錯誤)
*/
function deleteAll(string $dir, ?string $excludePath = null): bool
{
if (!is_dir($dir)) {
fwrite(STDERR, "錯誤:{$dir} 不是一個目錄\n");
return false;
}
$success = true;
$dh = @opendir($dir);
if ($dh === false) {
fwrite(STDERR, "錯誤:無法打開目錄 {$dir}\n");
return false;
}
while (($item = readdir($dh)) !== false) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $item;
// 如果設置了排除路徑且當前路徑與排除路徑相同,則跳過
// (注意:realpath() 會解析符號鏈接,可能導致排除行爲與預期不符,此處保留原邏輯未改動)
if ($excludePath !== null && realpath($path) === realpath($excludePath)) {
echo "跳過排除項: {$path}\n";
continue;
}
// 使用 lstat 一次性獲取文件類型(避免多次 is_link/is_dir 調用)
$stat = @lstat($path);
if ($stat !== false) {
// 確保常量可用(部分環境可能未預定義)
if (!defined('S_IFMT')) define('S_IFMT', 0170000);
if (!defined('S_IFDIR')) define('S_IFDIR', 0040000);
if (!defined('S_IFREG')) define('S_IFREG', 0100000);
if (!defined('S_IFLNK')) define('S_IFLNK', 0120000);
$mode = $stat['mode'] & S_IFMT;
if ($mode === S_IFLNK) {
// 符號鏈接:只刪除鏈接本身,不追蹤目標
if (unlink($path)) {
echo "已刪除符號鏈接: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除符號鏈接 {$path}\n");
$success = false;
}
continue;
}
if ($mode === S_IFDIR) {
// 目錄:遞歸刪除內容,再刪除空目錄
if (deleteAll($path, $excludePath)) {
if (rmdir($path)) {
echo "已刪除目錄: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除目錄 {$path}(可能權限不足或非空)\n");
$success = false;
}
} else {
$success = false;
}
continue;
}
if ($mode === S_IFREG) {
// 普通文件
if (unlink($path)) {
echo "已刪除文件: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除文件 {$path}\n");
$success = false;
}
continue;
}
// 其他類型(管道、套接字等),嘗試刪除
if (unlink($path)) {
echo "已刪除特殊文件: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除特殊文件 {$path}\n");
$success = false;
}
} else {
// lstat 失敗(如權限不足),回退到傳統判斷方式,儘可能刪除
if (is_link($path)) {
if (unlink($path)) {
echo "已刪除符號鏈接: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除符號鏈接 {$path}\n");
$success = false;
}
} elseif (is_dir($path)) {
if (deleteAll($path, $excludePath)) {
if (rmdir($path)) {
echo "已刪除目錄: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除目錄 {$path}(可能權限不足或非空)\n");
$success = false;
}
} else {
$success = false;
}
} else {
if (unlink($path)) {
echo "已刪除文件: {$path}\n";
} else {
fwrite(STDERR, "錯誤:無法刪除文件 {$path}\n");
$success = false;
}
}
}
}
closedir($dh);
return $success;
}
// 命令行運行檢查
if (PHP_SAPI !== 'cli') {
die("此腳本僅允許在命令行(CLI)模式下運行。\n");
}
// 獲取當前工作目錄(腳本執行時所在的目錄)
$currentDir = getcwd();
if ($currentDir === false) {
fwrite(STDERR, "錯誤:無法獲取當前工作目錄\n");
exit(1);
}
echo "當前工作目錄: {$currentDir}\n";
// 腳本自身的絕對路徑
$selfPath = realpath(__FILE__);
$excludeSelf = false;
// 判斷腳本自身是否位於當前工作目錄下
if ($selfPath !== false && strpos($selfPath, $currentDir . DIRECTORY_SEPARATOR) === 0) {
$excludeSelf = true;
echo "注意:腳本自身位於當前目錄,刪除時將排除該文件: " . basename($selfPath) . "\n";
} else {
echo "腳本自身不在當前目錄,將刪除當前目錄下的所有內容。\n";
}
// 安全確認提示
echo "\n⚠️ 警告:此操作將永久刪除當前目錄下的所有文件和文件夾,且不可恢復!\n";
echo "請輸入 'yes' 確認執行: ";
$handle = fopen("php://stdin", "r");
$confirm = trim(fgets($handle));
fclose($handle);
if ($confirm !== 'yes') {
echo "操作已取消。\n";
exit(0);
}
echo "\n開始刪除...\n";
$result = deleteAll($currentDir, $excludeSelf ? $selfPath : null);
if ($result) {
echo "\n✅ 刪除操作完成。\n";
if ($excludeSelf && file_exists($selfPath)) {
echo "注意:腳本自身未被刪除,位於 {$selfPath}\n";
echo "如需同時刪除腳本自身,請手動執行: unlink('{$selfPath}');\n";
}
} else {
echo "\n❌ 刪除過程中出現錯誤(部分文件/目錄可能因權限等原因未被刪除)。\n";
exit(1);
}安全特性
僅限 CLI 運行:防止通過 Web 訪問意外觸發。
交互確認:必須輸入
yes纔會執行刪除。自動排除自身:如果腳本位於當前工作目錄,刪除時會自動跳過,避免運行中異常。
符號鏈接安全:僅刪除鏈接本身,不追蹤鏈接目標,防止誤刪重要數據。
詳細日誌:每個刪除的文件/目錄都會輸出,錯誤信息會顯示到標準錯誤。
權限處理:遇到無權限刪除的文件會報錯並繼續,不會中斷整個流程。