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

时间:2010-11-16 07:46:15

标签: powershell stream

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

不幸的是,get-content | %{ whatever($_) }似乎在管道的这个阶段将整个行保留在内存中。它的速度也非常慢,需要花费很长时间来实际阅读。

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

  1. 如何让它逐行处理流,而不是将整个事物缓存在内存中?我想避免为此目的使用几次RAM。
  2. 如何让它跑得更快?在get-content上迭代的PowerShell似乎比C#脚本慢100倍。
  3. 我希望我在这里做一些愚蠢的事情,比如错过-LineBufferSize参数或某事......

3 个答案:

答案 0 :(得分:89)

如果您真的要处理多GB的文本文件,请不要使用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
}

答案 1 :(得分:49)

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 :(得分:5)

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

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