如何在 PowerShell 中将文件作为流逐行处理

问题描述 投票:0回答:4

我正在处理一些数 GB 的文本文件,并希望使用 PowerShell 对它们进行一些流处理。这很简单,只需解析每一行并提取一些数据,然后将其存储在数据库中。

不幸的是,

get-content | %{ whatever($_) }
似乎将管道此阶段的整组行保留在内存中。它的速度也出奇的慢,需要很长时间才能真正读完。

所以我的问题分为两部分:

  1. 如何让它逐行处理流而不将整个内容缓冲在内存中?我想避免为此目的耗尽几GB RAM。
  2. 如何让它运行得更快? PowerShell 迭代
    get-content
    似乎比 C# 脚本慢 100 倍。

我希望我在这里做了一些愚蠢的事情,比如缺少

-LineBufferSize
参数或其他东西......

powershell stream
4个回答
100
投票

如果您确实要处理数千兆字节的文本文件,请不要使用 PowerShell。即使您找到一种更快地读取它的方法,无论如何,在 PowerShell 中处理大量行都会很慢,并且您无法避免这种情况。即使是简单的循环也是昂贵的,比如说 1000 万次迭代(在你的例子中相当真实),我们有:

# "empty" loop: takes 10 seconds
measure-command { for($i=0; $i -lt 10000000; ++$i) {} }

# "simple" job, just output: takes 20 seconds
measure-command { for($i=0; $i -lt 10000000; ++$i) { $i } }

# "more real job": 107 seconds
measure-command { for($i=0; $i -lt 10000000; ++$i) { $i.ToString() -match '1' } }

更新:如果您仍然不害怕,那么尝试使用.NET阅读器:

$reader = [System.IO.File]::OpenText("my.log")
try {
    for() {
        $line = $reader.ReadLine()
        if ($line -eq $null) { break }
        # process the line
        $line
    }
}
finally {
    $reader.Close()
}

更新2

有关于可能更好/更短的代码的评论。带有

for
的原始代码没有任何问题,也不是伪代码。但阅读循环的较短(最短?)变体是

$reader = [System.IO.File]::OpenText("my.log")
while($null -ne ($line = $reader.ReadLine())) {
    $line
}

53
投票

System.IO.File.ReadLines()
非常适合这种场景。它返回文件的所有行,但让您立即开始迭代这些行,这意味着它不必将整个内容存储在内存中。

需要.NET 4.0或更高版本。

foreach ($line in [System.IO.File]::ReadLines($filename)) {
    # do something with $line
}

http://msdn.microsoft.com/en-us/library/dd383503.aspx


2
投票

如果您想直接使用 PowerShell,请查看以下代码。

$content = Get-Content C:\Users\You\Documents\test.txt
foreach ($line in $content)
{
    Write-Host $line
}

0
投票

对于那些感兴趣的人...

对此有一点看法,因为我必须处理非常大的文件。

以下是包含 5600 万行/记录的 39 GB xml 文件的结果。查找文本是一个 10 位数字

1) GC -rc 1000 | % -match -> 183 seconds
2) GC -rc 100 | % -match -> 182 seconds
3) GC -rc 1000 | % -like -> 840 seconds
4) GC -rc 100 | % -like -> 840 seconds
5) sls -simple -> 730 seconds
6) sls -> 180 seconds (sls default uses regex, but pattern in my case is passed as literal text)
7) Switch -file -regex - 258 seconds
8) IO.File.Readline - 250 seconds

1 和 6 是明显的赢家,但我选择了 1

© www.soinside.com 2019 - 2024. All rights reserved.