在Windows中基于修改日期删除数百万个文件的最有效方法

Wil*_*ill 4 powershell datetime delete-file memory-efficient

目标:使用脚本运行500万--1000万个XML文件并评估其日期,如果超过90天,则删除该文件.该脚本将每天运行.

问题:使用powershell Get-ChildItem -recurse会导致脚本锁定并且无法删除任何文件,我认为这是因为Get-ChildItem需要在对任何文件执行任何操作之前构建整个阵列.

解决方案?:经过大量研究后,我发现[System.IO.Directory] ​​:: EnumerateFiles能够在数组完全构建之前对数组中的项采取操作,这样可以提高效率(https:// msdn .microsoft.com/library/dd383458%28v = vs.100%29.aspx).经过更多测试后,我发现它foreach ($1 in $2)$1 | % {} 以前运行这个新代码更有效率并且可能再次崩溃这个服务器是否有任何调整任何人可以建议更有效的方法来编写脚本?

为了测试,我刚刚在15,000个目录中创建了15,000 x 0.02KB txt文件,其中包含随机数据并运行以下代码,我在$date变量上使用90秒而不是90天仅用于测试,删除所有txt需要6秒文件.

$getfiles = [System.IO.Directory]::EnumerateFiles("C:\temp", "*.txt", "AllDirectories")
$date = ([System.DateTime]::Now).AddSeconds(-90)
foreach ($2 in $getfiles) {
if ([System.IO.File]::GetLastWriteTime($2) -le $date) {
[System.IO.File]::Delete($2)
} #if
} #foreach
Run Code Online (Sandbox Code Playgroud)

Rob*_*t70 6

Powershell单线程,可处理100,000个文件> = 90天.

[IO.Directory]::EnumerateFiles("C:\FOLDER_WITH_FILES_TO_DELETE") |
select -first 100000 | where { [IO.File]::GetLastWriteTime($_) -lt
(Get-Date).AddDays(-90) } | foreach { rm $_ }
Run Code Online (Sandbox Code Playgroud)

或显示进度:

[IO.Directory]::EnumerateFiles("C:\FOLDER_WITH_FILES_TO_DELETE") |
select -first 100000 | where { [IO.File]::GetLastWriteTime($_) -lt
(Get-Date).AddDays(-90) } | foreach { $c = 0 } { Write-Progress
-Activity "Delete Files" -CurrentOperation $_ -PercentComplete 
((++$c/100000)*100); rm $_ }
Run Code Online (Sandbox Code Playgroud)

这适用于具有大量文件的文件夹.感谢我的同事道格!


Mat*_*sen 4

在开始删除文件之前,您可以通过$getfiles完全过滤数组来稍微调整它。

在 PowerShell 3.0 及更高版本中,您可以通过使用扩展方法来执行此操作,而无需使用管道(这确实会增加一些开销).Where({})

$date  = (Get-Date).AddDays(-90)
$files = [System.IO.Directory]::EnumerateFiles("C:\temp", "*.txt", "AllDirectories").Where({[System.IO.File]::GetLastWriteTime($_) -le $date})
foreach($file in $files)
{
    [System.IO.File]::Delete($file)
}
Run Code Online (Sandbox Code Playgroud)

因为你似乎并不关心它,所以最后一个微小的优化可能是完全放弃错误处理,直接调用 Windows API:

$Kernel32Util = Add-Type -MemberDefinition @'
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteFile(string filePath);
'@ -Name 'Kernel32Util' -Namespace 'NativeCode' -PassThru
Run Code Online (Sandbox Code Playgroud)

然后使用新的外部函数包装器执行与上面相同的操作,而不是[File]::Delete()

foreach($file in $files)
{
    [void]$Kernel32Util::DeleteFile($file)
}
Run Code Online (Sandbox Code Playgroud)

但在这一点上,我可能会退一步问这个问题:

“我是否使用了适合这项工作的正确工具?”

我(个人)的回答是:“可能不会”——是时候用编译语言(C#、F#、VB.NET)编写一个小实用程序了。

PowerShell 非常强大且有用,但以性能为代价 -这并不是一件坏事- 只是在决定使用什么工具来执行特定任务时值得考虑的事情:)