如何在PowerShell中迭代包含大量文件的文件夹?

时间:2012-09-05 02:26:08

标签: powershell

我正在尝试编写一个脚本,该脚本将遍历文件夹中的160万个文件,并根据文件名将它们移动到正确的文件夹中。

原因是NTFS无法处理单个文件夹中的大量文件而不会降低性能。

脚本调用“Get-ChildItem”来获取该文件夹中的所有项目,正如您所料,这会消耗大量内存(大约3.8 GB)。

我很好奇是否有其他方法可以遍历目录中的所有文件而不占用太多内存。

3 个答案:

答案 0 :(得分:13)

如果你这样做

$files = Get-ChildItem $dirWithMillionsOfFiles
#Now, process with $files

你将面临记忆问题。

使用PowerShell管道处理文件:

Get-ChildItem $dirWithMillionsOfFiles | %{ 
    #process here
}

第二种方式将消耗更少的内存,理想情况下不应超过某一点。

答案 1 :(得分:13)

如果需要减少内存占用,可以跳过使用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.GetDirectoriesDirectory.GetFiles,在您的情况下,它将生成1.6M元素数组,然后才能进行任何处理。一旦完成,那么是,管道的其余部分是流式传输。是的,这个初始的低级别部分具有相对最小的影响,因为它只是一个字符串数组,而不是一个富FileInfo个对象的数组。但声称在此模式中使用O(1)内存是不正确的。

相比之下,Powershell v3是基于.NET 4构建的,因此利用了我在上面提到的流API(Directory.EnumerateDirectoriesDirectory.EnumerateFiles)。这是一个很好的改变,有助于像你一样的场景。

答案 2 :(得分:0)

这就是我在不使用.Net 4.0的情况下实现它的方法。只有Powershell 2.0和老式的DIR命令:

只有2行(简单)代码:

cd <source_path>
cmd /c "dir /B"| % { move-item $($_) -destination "<dest_folder>" }

我的Powershell Proces仅使用15MB。旧的Windows 2008服务器上没有任何更改!

干杯!