【问题标题】:Delete files older than xx days删除超过 xx 天的文件
【发布时间】:2016-10-31 07:10:21
【问题描述】:

我需要以编程方式(最好使用 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 -first 100 那样将get-childitem 传递给SELECT?您需要发布您当前拥有的脚本。
  • 我已经写了一个简单的脚本来做这个也许你应该包括你到目前为止所尝试的。现在这读起来就像是一个关于 codez 的问题。在大多数情况下,我们在这里帮助修复或改进现有代码。即使你是“分批”做的,除非你使用工作或其他东西,否则你仍然会一次做一个。您在寻找进度指示吗?让它运行并做它的事情有什么害处?如果做得足够多,只有第一遍会很糟糕。我最近做了一项任务来执行此操作,但第一次运行删除了 500 万个文件需要几个小时。现在运行良好。
  • 如果您现有的代码有逻辑错误,也许我们可以修复这些错误并显示您可能出错的地方。
  • 您是否考虑过 robocopy?您可以将其配置为在完成后给您一个报告。
  • 我不在电脑附近,但如果没有其他人回答 robocopy 解决方案,我稍后会

标签: powershell filesystems delete-file


【解决方案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 放在那里。

【讨论】:

    【解决方案2】:

    要满足批量删除 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 的角度来看。
    • 看起来 select 会在 3.0 (documentation) 之前短路管道中的先前命令:“从 Windows PowerShell 3.0 开始,Select-Object 包含一个优化功能,可防止来自创建和处理未使用的对象的命令。” 不过,这里的一个问题是,在每个循环中,Get-ChildItem 命令将遍历在之前的每个循环中未删除的所有文件循环。
    • 代替粗略的$true/exit 条件,您可以轻松地使用$files.Count -GT 1 逻辑作为循环的退出条件。 do{#stuff}while($files.Count -gt 1)
    【解决方案3】:

    不要循环遍历所有文件并将标记为删除的文件存储在列表中,然后再次循环遍历列表中的每个文件,只需在找到它们时将每个文件通过管道传递到下一个命令。

    所以替换这个:

    $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... 还不如直接做"Deleting File $($_.FullName)"
    • @Matt 你得问问 OP 为什么。 ForEach 正文的内容与问题并没有密切关系,所以我保留了它的功能。
    • 这看起来不错。不知道为什么我使用 $DirectoryName,可能是深夜 :) 批处理只是为了让它更快,但我认为这可能有效。
    猜你喜欢
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-15
    • 2012-06-22
    • 1970-01-01
    • 2017-10-29
    • 2015-07-07
    相关资源
    最近更新 更多