2012-09-05 75 views
8

我正在尝试编写一个脚本,该脚本可以处理文件夹中的160万个文件,并根据文件名将它们移动到正确的文件夹中。如何迭代PowerShell中包含大量文件的文件夹?

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

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

我很好奇,如果有任何其他方式来遍历目录中的所有文件,而无需占用太多​​的内存。

回答

13

如果你

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

你将面临内存问题。

使用PowerShell管道处理的文件:

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

第二种方式将消耗更少的内存,最好应不会增长到某一点。

+0

感谢您的好和简单的解决方案。我一直认为在处理下一个函数之前,使用powershell进行流水线处理会返回整个结果。 –

+2

这实际上仍然需要'O(n)'内存,但如果它解决了问题,那么我同意这是最好的解决方案。 – latkin

12

如果您需要减少内存占用量,可以跳过使用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元素的数组。一旦完成,那么是的,管道的其余部分是流式传输。是的,这个最初的低级别作品的影响相对较小,因为它仅仅是一个字符串数组,而不是一组富有对象的数组。但声称在这种模式下使用内存是不正确的。

相反,Powershell v3构建于.NET 4之上,因此利用了上面提到的流式API(Directory.EnumerateDirectoriesDirectory.EnumerateFiles)。这是一个很好的变化,并且可以帮助您像您的情景一样。

+0

我认为像manojlds一样使用Get-ChildItem的管道会达到同样的效果,但是感谢您向我展示如何使用.Net和powershell! :)。 –

+0

是的,get-childitem | foreach-objetc {...}也将只处理一个通过的项目。 – x0n

+1

看我的编辑。 'get-childitem | foreach {...}'仅仅是伪流,它在技术上仍然需要'O(n)'内存。 – latkin

0

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

这只是2的(简单)的代码行:

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

我Powershell的理线只使用15MB。旧的Windows 2008服务器没有变化!

干杯!

相关问题