Add-Content、Set-Content 甚至 Out-File 在 PowerShell 中的速度非常慢。这是因为每次调用都会打开文件、写入文件并关闭句柄。它没有比这更聪明的事情了。
在您考虑管道如何与Get-ChildItem(以及Where-Object 和Select-Object)一起工作之前,这听起来还不错。它不会等到完成后才开始将对象传递到管道中。一旦提供者返回对象,它就会开始传递对象。对于大型结果集,这意味着对象在几个完成处理后很长时间仍在管道中馈送。一般来说,这很棒!这意味着系统将更有效地运行,这就是为什么这样的东西:
$x = Get-ChildItem;
$x | ForEach-Object { [...] };
比这样的东西慢得多:
Get-ChildItem | ForEach-Object { [...] };
这就是为什么像这样的东西似乎停滞不前的原因:
Get-ChildItem | Sort-Object Name | ForEach-Object { [...] };
Sort-Object cmdlet 需要等待,直到它收到所有管道对象后才能进行排序。它必须能够排序。排序本身几乎是瞬时的。它只是等待获得完整结果的 cmdlet。
Add-Content 的问题在于,它对管道的体验不是“这是一个要写一次的大字符串”,而是“这是一个要写的字符串。这是一个要写的字符串。这是一个要写的字符串。这是要写的字符串。”您将在此处逐行向Add-Content 发送内容。 每一行都会实例化一个对Add-Content的新调用,要求打开、写入和关闭文件。如果将Get-ChildItem [...] | Where-Object [...] 的结果分配给一个变量,然后将整个变量一次写入文件,您可能会看到更好的性能:
$limit = (Get-Date).AddDays(-15);
$path = "E:\Data\PathToScratch.txt";
$scratchpath = Get-Content $path -TotalCount 1;
$Results = Get-ChildItem -Path $scratchpath -Recurse -Force -Directory | `
Where-Object{$_.CreationTime -lt $limit } | `
Select-Object -ExpandPropery FullName;
Add-Content C:\Data\eProposal\POC\ScratchContents.txt -Value $Results;
但是,如果您的结果实际上非常大,您可能会担心内存使用情况。您实际上也可以为此目的使用System.IO.StreamWriter。通过切换到StreamWriter,我的流程速度提高了近两个数量级(从 12 小时到 20 分钟),并且只在我有大约 250 行要编写时才调用StreamWriter(这似乎是收支平衡点StreamWriter 的开销)。但我正在为大约 10,000 个用户和近 10 TB 的数据解析用户主目录和组共享的所有 ACL。您的任务可能没有那么大。
Here 是一个很好的博客来解释这个问题。