powershell stream

Cómo procesar un archivo en PowerShell línea por línea como una secuencia



stream (3)

Estoy trabajando con algunos archivos de texto de varios gigabytes y quiero hacer un poco de procesamiento de flujo con PowerShell. Es algo simple, solo analizar cada línea y extraer algunos datos, luego almacenarlos en una base de datos.

Desafortunadamente, get-content | %{ whatever($_) } get-content | %{ whatever($_) } parece mantener el conjunto completo de líneas en esta etapa de la tubería en la memoria. También es sorprendentemente lento, tardando mucho tiempo en leerlo todo.

Entonces mi pregunta es dos partes:

  1. ¿Cómo puedo hacer que procese el flujo línea por línea y no mantener toda la información almacenada en la memoria? Me gustaría evitar usar varias gigas de RAM para este propósito.
  2. ¿Cómo puedo hacer que funcione más rápido? La repetición de PowerShell sobre un get-content parece ser 100 veces más lenta que una secuencia de comandos C #.

Espero que haya algo tonto que estoy haciendo aquí, como perder un parámetro -LineBufferSize o algo así ...


Si desea utilizar PowerShell directamente, consulte el siguiente código.

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


Si realmente va a trabajar en archivos de texto de varios gigabytes, no use PowerShell. Incluso si encuentra una manera de leerlo, un procesamiento más rápido de una gran cantidad de líneas será lento en PowerShell de todos modos y no podrá evitarlo. Incluso los bucles simples son costosos, digamos por 10 millones de iteraciones (bastante reales en su caso) tenemos:

# "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'' } }

ACTUALIZACIÓN: Si todavía no tienes miedo, intenta usar el lector de .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() }

ACTUALIZACIÓN 2

Hay comentarios sobre posiblemente un código mejor / más corto. No hay nada de malo con el código original con for y no es pseudo-código. Pero la variante más corta (¿la más corta?) Del ciclo de lectura es

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


System.IO.File.ReadLines() es perfecto para este escenario. Devuelve todas las líneas de un archivo, pero le permite comenzar a iterar sobre las líneas inmediatamente, lo que significa que no tiene que almacenar todo el contenido en la memoria.

Requiere .NET 4.0 o superior.

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

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