Powershell 在windows中,根据修改日期删除数百万个文件的最有效方法

Powershell 在windows中,根据修改日期删除数百万个文件的最有效方法,powershell,datetime,delete-file,memory-efficient,Powershell,Datetime,Delete File,Memory Efficient,目标:使用脚本运行500万到1000万个XML文件并评估其日期,如果超过90天,则删除该文件。脚本将每天运行 问题:使用powershell Get ChildItem-recurse会导致脚本锁定并无法删除任何文件,我认为这是因为Get ChildItem需要在对任何文件执行任何操作之前构建整个数组 解决方案?:经过大量研究,我发现[System.IO.Directory]::EnumerateFiles将能够在数组完全构建之前对数组中的项执行操作,从而使事情更加高效()。经过更多的测试,我发

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

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

解决方案?:经过大量研究,我发现[System.IO.Directory]::EnumerateFiles将能够在数组完全构建之前对数组中的项执行操作,从而使事情更加高效()。经过更多的测试,我发现
foreach($2中的1)
$1 |%{}
在我运行这段新代码并可能再次崩溃这台服务器之前,有没有任何人可以建议对脚本编写进行更有效的调整

为了进行测试,我在15000个目录中创建了15000 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

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

在PowerShell 3.0及更高版本中,您可以通过使用
。其中({})
扩展方法在不使用管道的情况下执行此操作(这确实增加了一些开销):

$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)
}

由于您似乎并不关心它,最后一个微小的优化可能是完全放弃错误处理,直接调用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
然后使用新的外部函数包装器执行上述操作,而不是使用
[文件]::Delete()


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

“我使用的工具是否适合这份工作?” 我(个人)的答案是:“可能不会”——是时候用编译语言(C#,F#,VB.NET)编写一个小实用程序了


PowerShell功能强大且有用,但以性能为代价—这不是一件坏事—在决定用于特定任务的工具时,这只是一件值得考虑的事情:)

PowerShell一个可处理100000个文件的线性程序>=90天

[IO.Directory]::EnumerateFiles("C:\FOLDER_WITH_FILES_TO_DELETE") |
select -first 100000 | where { [IO.File]::GetLastWriteTime($_) -lt
(Get-Date).AddDays(-90) } | foreach { rm $_ }
或有进展:

[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 $_ }

这适用于包含大量文件的文件夹。多亏了我的同事道格

对于不同版本的powershell,我最终得到了几个稍有不同的代码

#If powershell version is >3
$date = ([System.DateTime]::Now).AddDays(-30)
foreach ($2 in ([System.IO.Directory]::EnumerateFiles("D:\Folder to cleanup", "*.*", "AllDirectories").Where({[System.IO.File]::GetLastWriteTime($_) -le $date}))) {
[System.IO.File]::Delete($2)
} #foreach

#IF powershell version is >2.0 <3.0
$date = ([System.DateTime]::Now).AddDays(-30)
foreach ($2 in ([System.IO.Directory]::EnumerateFiles("D:\Folder to cleanup", "*.*", "AllDirectories"))) {
if ([System.IO.File]::GetLastWriteTime($2) -le $date) {
[System.IO.File]::Delete($2)
} #if
} #foreach

#IF powershell version is 2.0
$date = ([System.DateTime]::Now).AddDays(-30)
foreach ($2 in ([System.IO.Directory]::GetFiles("D:\Folder to cleanup", "*.*", "AllDirectories"))) {
if ([System.IO.File]::GetLastWriteTime($2) -le $date) {
[System.IO.File]::Delete($2)
} #if
} #foreach
#如果powershell版本>3
$date=([System.DateTime]::Now).AddDays(-30)
foreach($2 in([System.IO.Directory]::枚举文件(“D:\Folder to cleanup”、“****”、“AllDirectories”)。其中({[System.IO.File]::GetLastWriteTime($)-le$date}){
[System.IO.File]::删除($2)
}#foreach

#如果powershell版本>2.0(如果您还没有这样做),则可能会有帮助。首先阅读所有警告。做一两次备份。使用风险自负。只要将enumeratefiles的输出保存到一个变量,ienumerable就不会给您带来任何好处,因为PS将在继续之前等待该行完成(它不是异步方法)。你需要直接在循环、管道或类似的东西中使用它。我确实让一个朋友编写了一个windows窗体应用程序,对删除的每个文件进行传统的日志记录,这样做速度会慢很多,不确定他用来构建数组和删除文件的代码是什么,但我确实提到了效率的需要。我喜欢过滤数组中处理的最大文件数的想法,但可能没有必要,因为[System.IO.Directory]::EnumerateFiles可以在构建数组时处理数组中的文件。同时为每个文件调用(Get Date).AddDays(-90)是无效的。这应该是一个静态变量。
#If powershell version is >3
$date = ([System.DateTime]::Now).AddDays(-30)
foreach ($2 in ([System.IO.Directory]::EnumerateFiles("D:\Folder to cleanup", "*.*", "AllDirectories").Where({[System.IO.File]::GetLastWriteTime($_) -le $date}))) {
[System.IO.File]::Delete($2)
} #foreach

#IF powershell version is >2.0 <3.0
$date = ([System.DateTime]::Now).AddDays(-30)
foreach ($2 in ([System.IO.Directory]::EnumerateFiles("D:\Folder to cleanup", "*.*", "AllDirectories"))) {
if ([System.IO.File]::GetLastWriteTime($2) -le $date) {
[System.IO.File]::Delete($2)
} #if
} #foreach

#IF powershell version is 2.0
$date = ([System.DateTime]::Now).AddDays(-30)
foreach ($2 in ([System.IO.Directory]::GetFiles("D:\Folder to cleanup", "*.*", "AllDirectories"))) {
if ([System.IO.File]::GetLastWriteTime($2) -le $date) {
[System.IO.File]::Delete($2)
} #if
} #foreach