2012-01-17 6 views
6

Tengo la necesidad de analizar a través de un archivo grande delimitado por tuberías para contar el número de registros cuya 5ª columna cumple y no cumple mis criterios.Necesito ayuda para mejorar el rendimiento de la secuencia de comandos de análisis de texto delimitado de PowerShell

PS C:\temp> gc .\items.txt -readcount 1000 | ` 
    ? { $_ -notlike "HEAD" } | ` 
    % { foreach ($s in $_) { $s.split("|")[4] } } | ` 
    group -property {$_ -ge 256} -noelement | ` 
    ft –autosize 

Este comando hace lo que yo quiero, volviendo salida como esta:

 
    Count Name 
    ----- ---- 
1129339 True 
2013703 False 

Sin embargo, para un archivo de prueba de 500 MB, este comando se lleva alrededor de 5,5 minutos para correr según lo medido por la Medida-Command. Un archivo típico tiene más de 2 GB, donde esperar más de 20 minutos es indeseablemente largo.

¿Ve alguna manera de mejorar el rendimiento de este comando?

Por ejemplo, ¿hay alguna manera de determinar un valor óptimo para ReadCount Get-Content? Sin él, toma 8,8 minutos completar el mismo archivo.

+0

¿Has probado StreamReader? Creo que Get-Content carga todo el archivo en la memoria antes de que haga nada con él. – Gisli

+0

¿Quiere decir al importar System.IO? – neontapir

+0

Sí, use .net framework si puede. Solía ​​leer grandes archivos de registro que SQL Server genera con buenos resultados. No conozco otra forma en PowerShell para leer archivos de gran tamaño de manera eficiente, pero no soy un experto. – Gisli

Respuesta

4

¿Has probado StreamReader? Creo que Get-Content carga todo el archivo en la memoria antes de que haga nada con él.

StreamReader class

4

Usando @ indicio de Gisli, aquí está la secuencia de comandos que terminó con:

param($file = $(Read-Host -prompt "File")) 
$fullName = (Get-Item "$file").FullName 
$sr = New-Object System.IO.StreamReader("$fullName") 
$trueCount = 0; 
$falseCount = 0; 
while (($line = $sr.ReadLine()) -ne $null) { 
     if ($line -like 'HEAD|') { continue } 
     if ($line.split("|")[4] -ge 256) { 
      $trueCount++ 
     } 
     else { 
      $falseCount++ 
     } 
} 
$sr.Dispose() 
write "True count: $trueCount" 
write "False count: $falseCount" 

Se obtienen los mismos resultados en aproximadamente un minuto, que cumple con mis requisitos de rendimiento.

2

Simplemente añadiendo otro ejemplo usando StreamReader para leer un gran archivo de registro IIS y generar todas las direcciones IP exclusivas del cliente y algunas métricas de perfomance.

$path = 'A_245MB_IIS_Log_File.txt' 
$r = [IO.File]::OpenText($path) 

$clients = @{} 

while ($r.Peek() -ge 0) { 
    $line = $r.ReadLine() 

    # String processing here... 
    if (-not $line.StartsWith('#')) { 
     $split = $line.Split() 
     $client = $split[-5] 
     if (-not $clients.ContainsKey($client)){ 
      $clients.Add($client, $null) 
     } 
    } 
} 

$r.Dispose() 
$clients.Keys | Sort 

Una pequeña comparación de rendimiento contra Get-Content:

StreamReader: Completo: 5,5 segundos, PowerShell.exe: 35.328 KB de RAM.

Get-Content: Completado: 23.6 segundos. PowerShell.exe: 1,110,524 KB de RAM.

Cuestiones relacionadas