我正在尝试编写一个脚本,该脚本可以处理文件夹中的160万个文件,并根据文件名将它们移动到正确的文件夹中。如何迭代PowerShell中包含大量文件的文件夹?
原因是NTFS不能处理单个文件夹中的大量文件而不会降低性能。
的脚本调用“GET-ChildItem”来获得该文件夹中的所有项目,如你所料,这会消耗大量的内存(约3.8 GB)。
我很好奇,如果有任何其他方式来遍历目录中的所有文件,而无需占用太多的内存。
我正在尝试编写一个脚本,该脚本可以处理文件夹中的160万个文件,并根据文件名将它们移动到正确的文件夹中。如何迭代PowerShell中包含大量文件的文件夹?
原因是NTFS不能处理单个文件夹中的大量文件而不会降低性能。
的脚本调用“GET-ChildItem”来获得该文件夹中的所有项目,如你所料,这会消耗大量的内存(约3.8 GB)。
我很好奇,如果有任何其他方式来遍历目录中的所有文件,而无需占用太多的内存。
如果你
$files = Get-ChildItem $dirWithMillionsOfFiles
#Now, process with $files
你将面临内存问题。
使用PowerShell管道处理的文件:
Get-ChildItem $dirWithMillionsOfFiles | %{
#process here
}
第二种方式将消耗更少的内存,最好应不会增长到某一点。
如果您需要减少内存占用量,可以跳过使用Get-ChildItem
,而是直接使用.NET API。我假设您使用Powershell v2,如果是这样的话,请按照步骤here的步骤启用.NET 4以加载Powershell v2。
在.NET 4中,有一些不错的API用于列举文件和目录,而不是在数组中返回它们。
[IO.Directory]::EnumerateFiles("C:\logs") |%{ <move file $_> }
通过使用此API,而不是[IO.Directory]::GetFiles()
,只有一个文件名会在同一时间进行处理,所以内存的消耗应该是比较小的。
编辑
我也假设你试过像Get-ChildItem |ForEach { process }
一个简单的流水线方式。如果这足够了,我同意这是一条路。
但是我想澄清一个常见的误解:在v2中,Get-ChildItem
(或者真的,文件系统提供者)确实是而不是真正的流。该实现使用API Directory.GetDirectories
和Directory.GetFiles
,在您的情况下,将在发生任何处理之前生成一个1.6M元素的数组。一旦完成,那么是的,管道的其余部分是流式传输。是的,这个最初的低级别作品的影响相对较小,因为它仅仅是一个字符串数组,而不是一组富有对象的数组。但声称在这种模式下使用内存是不正确的。
相反,Powershell v3构建于.NET 4之上,因此利用了上面提到的流式API(Directory.EnumerateDirectories
和Directory.EnumerateFiles
)。这是一个很好的变化,并且可以帮助您像您的情景一样。
这就是我在不使用.Net 4.0的情况下实现它的原因。只有Powershell 2。0和老式的DIR命令:
这只是2的(简单)的代码行:
cd <source_path>
cmd /c "dir /B"| % { move-item $($_) -destination "<dest_folder>" }
我Powershell的理线只使用15MB。旧的Windows 2008服务器没有变化!
干杯!
感谢您的好和简单的解决方案。我一直认为在处理下一个函数之前,使用powershell进行流水线处理会返回整个结果。 –
这实际上仍然需要'O(n)'内存,但如果它解决了问题,那么我同意这是最好的解决方案。 – latkin