删除 xx 天以前的文件

3
我需要以编程方式(最好使用Powershell)删除一个文件夹中早于给定天数的一些文件。
我已经编写了一个简单的脚本来完成这个任务,但是我遇到的问题是,由于文件太多,它似乎甚至无法开始删除。
我希望找到一种分批删除的方法。比如说先获取前1000个文件,然后删除,以此类推。
目前,该文件夹可能有几十万个文件,而且几乎不可能遍历。
Param(
  [Parameter(Mandatory=$true)][string]$Path,
  [Parameter(Mandatory=$true)][string]$DaysToDelete
)

$limit = (Get-Date).AddDays($DaysToDelete)
$LogFile = "FileCleanupLog-$(Get-Date -f yyyyMMdd_HH_mm_ss).txt";

function Log-Message
{
   Param ([string]$logtext)
   Add-content $LogFile -value $logtext
}

If (-Not (Test-Path $Path))
{
    Write-Host "Invalid Path provided!" -ForegroundColor Red
    Exit
}

$files = Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit }

If ($files.Count -GT 1) {
    $files | % {$directory=$_.DirectoryName;(Log-Message "Deleting File $directory\$_");$_ } | Remove-Item -Force 
}

你尝试过将get-childitem管道传输到SELECT中吗?例如get-childitem . | select -first 100?你需要发布你当前拥有的脚本。 - Tony Hinkle
如果您现有的代码存在逻辑错误,也许我们可以修复并指出您可能犯错的地方。 - Matt
抱歉,我当时在用手机,很难贴代码...现在我已经添加了代码。我让它运行了一个小时,但它甚至还没有进入if语句。我想要分批处理,在while循环或类似的东西中进行,这样我就可以看到一些进展了。 - TaylorN
1
你考虑过使用Robocopy吗?你可以配置它在完成后给你生成一份报告。 - Matt
1
我现在不在电脑旁,但如果没有其他人提供 robocopy 解决方案,我稍后会回答。 - Matt
显示剩余2条评论
3个回答

2

不要循环所有文件并将标记为删除的文件存储在列表中,然后再循环列表中的每个文件,而是在找到它们时将每个文件直接传输给下一个命令。

因此,将其替换为:

$files = Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit }

If ($files.Count -GT 1) {
    $files | % {$directory=$_.DirectoryName;(Log-Message "Deleting File $directory\$_");$_ } | Remove-Item -Force 
}

使用类似以下的代码:

Get-ChildItem -Path $Path -Recurse -Force `
| Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } `
| % {
    $directory=$_.DirectoryName
    (Log-Message "Deleting File $directory\$_")
    $_ } `
| Remove-Item -Force

这里没有太大的收益,但为什么要麻烦地使用 $directory=$_.DirectoryName...不如直接使用 "正在删除文件 $($_.FullName)" - Matt
@Matt 你需要问一下原帖作者为什么要这样做。ForEach 循环体的内容与问题本身无关,所以我保持了其功能上的等效性。 - Jason Boyd
这看起来实际上很不错。不确定为什么我使用了$DirectoryName,可能是深夜的缘故 :) 批处理只是为了让它更快,但我认为这可能有效。 - TaylorN

1
为了满足您批量删除1000个文件的标准,请使用以下方法。 select -first 1000 将导致它每次通过 while 循环仅删除1000个文件。
while($true){
    $files = Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $limit } | select -first 1000
    If ($files.Count -GT 1) {
        $files | % {$directory=$_.DirectoryName;(Log-Message "Deleting File $directory\$_");$_ } | Remove-Item -Force 
    } else {
        exit 
    }
}

我不知道这是否会更快——这取决于PowerShell是否足够智能,在找到前1000个文件后停止get-childitem。


这正是我所想的。我不确定批处理是否会使事情更快。根据我的经验,它确实可以,但这更多来自于 SQL/.NET 的角度。 - TaylorN
1
根据3.0版的文档,select似乎会短路管道中之前的命令:*"从Windows PowerShell 3.0开始,Select-Object包括一种优化功能,可防止命令创建和处理未使用的对象。"* 然而,这里有一个问题,在每个循环内,Get-ChildItem命令将遍历在之前的每个循环中未被删除的所有文件。 - Jason Boyd
不要使用粗糙的$true/exit条件,你可以很容易地将$files.Count -GT 1逻辑作为循环的退出条件。do{#stuff}while($files.Count -gt 1) - Matt

1

我必须承认,我对我想要robocopy工作方式的理解有些错误。虽然它可以删除文件,但在被告知时,它仍然必须执行复制操作。因此,最好在目标机器上运行此建议,而不是使用UNC路径。除了失望之外,我仍然认为这是一个可行的解决方案。主要问题在于,robocopy将只选择我们需要的文件,而无需进行任何后处理。

$sourceDirectory = "D:\temp\New folder"
$dummyDirectory = "D:\temp\trashbin"
$loggingFile = "D:\temp\FileCleanupLog-$(Get-Date -f yyyyMMdd_HH_mm_ss).txt"

# Build the dummy directory. It will be deleted in the end.
New-Item -Path $dummyDirectory -ItemType Directory | Out-Null

& robocopy.exe $sourceDirectory /njh /ndl /nc /njs /minage:$days /mov /e /ns /np /l | Set-Content $loggingFile

# Purge the dummy directory with all the content we don't want
Remove-Item -Path $dummyDirectory -Force -Confirm:$false -Recurse

这里是所有开关的含义。大多数用于清理日志输出。日志应该只有一个被删除的完整路径列表。目前不会影响目录结构。如果需要,可以通过更改开关来解决此问题。您还将看到/l仅用于记录日志。您可以使用该开关测试要删除的文件是否正确。对于实际生产测试,您需要将其删除。
/minage:N        指定最小文件年龄(排除比N天或日期新的文件)。
/njh             指定没有作业头。
/njs             指定没有作业摘要。
/l               仅列出文件(而不是复制、删除或时间戳)。
/mov             移动文件,并在复制后从源中删除它们。
/ndl             指定不记录目录名称。
/nc              指定不记录文件类别。
/np              指定不显示复制操作的进度(迄今已复制的文件或目录数量)。
如果不在屏幕上显示数据,这也会使其执行得更快。这就是为什么我特别放了/np在里面。

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