Powershell:使用 'Out-File' 写入较小的文件需要 30 倍的时间

Powershell: smaller file takes 30x longer to write with 'Out-File'

我正在获取文件的内容并只保留与正则表达式或空行匹配的行。但是写结果,例如数据量较小,需要很长时间... 这是有问题的代码(我为 debugging/measuring 添加了几行):

$original = Get-Content "$localDir$ldif_file"
(Measure-Command -Expression { $original | Out-File "$localDir\Original-$ldif_file" }).TotalSeconds
$lines = ($original | Measure-Object -Line).Lines
"lines of `$original = $lines"

# Just keep lines of interest:
$stripped = $original | select-string -pattern '^custom[A-Z]','^$' -CaseSensitive
$lines = ($stripped | Measure-Object -Line).Lines
"lines of `$stripped = $lines"
(Measure-Command -Expression { $stripped | Out-File "$localDir\Stripped-$ldif_file" }).TotalSeconds

"done"

问题:将较小的($stripped)数据写入文件需要 342 秒(大约是 $original 数据的 30 倍)!请参阅下面的输出:

11.5371677
lines of $original = 188715
lines of $stripped = 126404
342.6769547
done

为什么 $stripped 的 Out-File 比 $original 的慢很多? 如何改进?

谢谢!

你真的是在比较苹果和橘子。

$original 包含 189K 个 字符串 ,但是 $stripped 包含 126K MatchInfo 个对象,这些对象必须在管道。

改为使用 -cmatch 运算符保留原始字符串输入值,您会发现将它们输出到文件要快得多:

$original = Get-Content "$localDir$ldif_file"
(Measure-Command -Expression { $original | Out-File "$localDir\Original-$ldif_file" }).TotalSeconds
$lines = ($original | Measure-Object -Line).Lines
"lines of `$original = $lines"

# Just keep lines of interest:
$stripped = $original |Where-Object {$_ -cmatch '^custom[A-Z]' -or $_ -like ''}
$lines = ($stripped | Measure-Object -Line).Lines
"lines of `$stripped = $lines"
(Measure-Command -Expression { $stripped | Out-File "$localDir\Stripped-$ldif_file" }).TotalSeconds

"done"

补充:

  • PowerShell 7+中,Select-String现在支持-Raw开关输出只是字符串(匹配行),这会大大加快命令的速度。

    • Windows PowerShell中,效率较低,您可以附上Select-String 调用 (...).Line 以仅将行作为字符串获取。

    • 另请注意,如果直接传递 文件路径Select-String 会快得多(以便它自己读取文件)而不是通过 Get-Content.

      管道传输单独的行
  • 一般来说,对于写入已经是字符串的对象Set-Content是更好更快的选择Out-File.

    相比
    • 有关性能比较,请参阅 this answer for background information and the bottom section of

    • 字符编码注意事项(请参阅 了解背景):

      • WindowsPowerShell中,Set-Content默认为ANSI编码,而Out-File默认为“Unicode”(UTF-16LE);根据需要使用 -Encoding
      • 幸运的是,PowerShell [Core] 6+ 使用一致的默认值,即没有 BOM 的 UTF-8。
  • 通过管道传递集合可能会很慢; 对于集合已经在内存中完整,将它们作为一个整体作为一个整体,作为一个argument - 假设目标 cmdlet 支持 - Set-Content-Value 参数 .

总而言之:

# *PowerShell 7*: Use -Raw to directly get the lines as string.
$stripped = $original | 
  Select-String -Raw -Pattern '^custom[A-Z]','^$' -CaseSensitive

# *Windows PowerShell*: Use (...).Line to get the lines as strings.
$stripped = ($original | 
  Select-String -Pattern '^custom[A-Z]','^$' -CaseSensitive).Line

$lines = $stripped.Count # Simply count the array elements == number of lines.
"lines of `$stripped = $lines"

(Measure-Command -Expression { 
  Set-Content "$localDir\Stripped-$ldif_file" -Value $stripped
}).TotalSeconds