Как обрабатывать файл в PowerShell построчно как поток - PullRequest
86 голосов
/ 16 ноября 2010

Я работаю с несколькими мультигигабайтными текстовыми файлами и хочу выполнить некоторую потоковую обработку на них с помощью PowerShell.Это простая штука, просто разбирая каждую строку и извлекая некоторые данные, затем сохраняя их в базе данных.

К сожалению, get-content | %{ whatever($_) }, похоже, сохраняет весь набор строк на этом этапе конвейера в памяти.Он также удивительно медленный, и на его прочтение уходит очень много времени.

Итак, мой вопрос состоит из двух частей:

  1. Как заставить его обрабатывать поток построчнои не хранить все это в буфере в памяти?Я хотел бы избежать использования нескольких гигабайт оперативной памяти для этой цели.
  2. Как я могу заставить его работать быстрее?Итерация PowerShell по get-content кажется в 100 раз медленнее, чем сценарий C #.

Я надеюсь, что здесь происходит что-то глупое, например, пропущен параметр -LineBufferSize или что-то еще ...

Ответы [ 3 ]

89 голосов
/ 16 ноября 2010

Если вы действительно собираетесь работать с текстовыми файлами объемом несколько гигабайт, не используйте PowerShell.Даже если вы найдете способ прочитать его быстрее, обработка большого количества строк в PowerShell все равно будет медленной, и вы не можете избежать этого.Даже простые циклы стоят дорого, скажем, для 10 миллионов итераций (вполне реально в вашем случае) у нас есть:

# "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
}
49 голосов
/ 14 октября 2012

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

4 голосов
/ 08 июля 2014

Если вы хотите использовать прямой PowerShell, проверьте приведенный ниже код.

$content = Get-Content C:\Users\You\Documents\test.txt
foreach ($line in $content)
{
    Write-Host $line
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...