目标:使用脚本运行500万--1000万个XML文件并评估其日期,如果超过90天,则删除该文件。该脚本将每天运行。
问题:使用powershell Get-ChildItem -recurse导致脚本锁定并且无法删除任何文件,我认为这是因为Get-ChildItem在对任何文件执行任何操作之前需要构建整个数组的方式
解决方案?:经过大量研究后,我发现[System.IO.Directory] :: EnumerateFiles能够在数组完全构建之前对数组中的项采取操作,这样可以提高效率({{ 3}})。经过更多测试后,我发现foreach ($1 in $2)
比$1 | % {}
效率更高
在我运行这个新代码并且可能再次崩溃这个服务器之前,是否有人可以建议任何调整以更有效的方式编写脚本?
为了测试,我刚刚在15,000个目录中创建了15,000 x 0.02KB txt文件,其中包含随机数据并运行以下代码,我在$date
变量上使用了90秒而不是90天仅用于测试,它花了6秒钟删除了所有的txt文件。
$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
答案 0 :(得分:6)
Powershell one-liner,可处理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 $_ }
或显示进度:
[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 $_ }
这适用于包含大量文件的文件夹。感谢我的同事道格!
答案 1 :(得分:3)
您可以通过在开始删除文件之前完全过滤$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)
}
因为你似乎并不关心它,所以最终的小优化可能是彻底摆脱错误处理并直接调用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
然后使用新的外部函数包装器而不是[File]::Delete()
执行与上面相同的操作:
foreach($file in $files)
{
[void]$Kernel32Util::DeleteFile($file)
}
在这一点上,我可能会退后一步并提出问题:
我的(个人)答案是:"可能不是" - 用编译语言(C#,F#,VB.NET)编写一个小实用程序的时间。
PowerShell非常强大且实用,但是以性能为代价 - 这不是一件坏事 - 在决定使用什么工具时,它只是值得考虑的事情用于特定任务:)
答案 2 :(得分:1)
对于不同版本的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