Estou trabalhando com alguns arquivos de texto de vários gigabytes e quero fazer algum processamento de stream neles usando o PowerShell. É uma coisa simples, apenas analisar cada linha e extrair alguns dados, em seguida, armazená-los em um banco de dados.
Infelizmente, get-content | %{ whatever($_) }
parece manter todo o conjunto de linhas neste estágio do tubo na memória. Também é surpreendentemente lento, levando muito tempo para realmente ler tudo.
Portanto, minha pergunta tem duas partes:
- Como posso fazer com que ele processe o fluxo linha por linha e não mantenha tudo armazenado na memória? Eu gostaria de evitar o uso de vários GB de RAM para essa finalidade.
- Como posso fazê-lo funcionar mais rápido? A iteração do PowerShell em um
get-content
parece ser 100x mais lenta do que um script C #.
Espero que haja algo estúpido que estou fazendo aqui, como perder um -LineBufferSize
parâmetro ou algo assim ...
powershell
stream
Scobi
fonte
fonte
get-content
, defina -ReadCount como 512. Observe que, neste ponto, $ _ no Foreach será uma matriz de strings.Get-Content
a uma variável, pois isso carregará o arquivo inteiro na memória. Por padrão, em um pipleline,Get-Content
processa o arquivo uma linha de cada vez. Contanto que você não esteja acumulando os resultados ou usando um cmdlet que se acumula internamente (como Sort-Object e Group-Object), a ocorrência de memória não deve ser tão ruim. Foreach-Object (%) é uma maneira segura de processar cada linha, uma de cada vez.get-content | % -End { }
, ele reclamará porque você não forneceu um bloco de processo. Portanto, ele não pode estar usando -End por padrão, ele deve estar usando -Process por padrão. E tente1..5 | % -process { } -end { 'q' }
ver que o bloco final só acontece uma vez, o normalgc | % { $_ }
não funcionaria se o scriptblock fosse -End ...Respostas:
Se você estiver realmente prestes a trabalhar com arquivos de texto de vários gigabytes, não use o PowerShell. Mesmo se você encontrar uma maneira de lê-lo, o processamento mais rápido de uma grande quantidade de linhas será lento no PowerShell de qualquer maneira e você não pode evitar isso. Mesmo loops simples são caros, digamos, para 10 milhões de iterações (bastante reais no seu caso), temos:
ATUALIZAÇÃO: se ainda não tiver medo, tente usar o leitor .NET:
ATUALIZAÇÃO 2
Há comentários sobre códigos possivelmente melhores / mais curtos. Não há nada de errado com o código original
for
e não é um pseudocódigo. Mas a variante mais curta (mais curta?) Do ciclo de leitura éfonte
do { $line = $reader.ReadLine(); $line } while ($line -neq $null)
for ( $line = $reader.ReadLine(); $line -ne $null; $line = $reader.ReadLine() ) { $line }
while($null -ne ($line = $read.ReadLine())) {$line}
. Mas o assunto não é realmente sobre essas coisas.System.IO.File.ReadLines()
é perfeito para este cenário. Ele retorna todas as linhas de um arquivo, mas permite que você comece a iterar sobre as linhas imediatamente, o que significa que não é necessário armazenar todo o conteúdo na memória.Requer .NET 4.0 ou superior.
http://msdn.microsoft.com/en-us/library/dd383503.aspx
fonte
Se você quiser usar o PowerShell direto, verifique o código abaixo.
fonte
Get-Content
é muito lento em arquivos grandes.