Realizando el análogo de PowerShell de *nix grep

Realizando el análogo de PowerShell de *nix grep

¿Hay alguna manera de optimizar el código de PowerShell a continuación (combina líneas particulares por cadena contenidas en un grupo de archivos de texto en una sola)?

$ErrorActionPreference = "Continue"
Start-Transcript -path D:\0xAC1CC07A.log -append
$OutFile = "D:\0xAC1CC07A.txt"
echo "filtering 0xAC1CC07A"
ForEach ($filenm in ((get-childitem -Path D:\FILES\* -include ubuntlive1mb_?????_201509*.txt -recurse -force))) 
{
 $filenm.fullName;
 (Get-Content $filenm) | select-string "0xAC1CC07A" | Add-Content $OutFile
}
Stop-Transcript

Funciona bien en cargas de trabajo pequeñas, pero en archivos de texto de 160 KB (más de 200 GB en total) funciona más de 4 días en mi máquina virtual Win2008R2. Sorprendentemente, Ubuntu 14.04 en hardware virtual similar hizo el trabajo en 4 horas:

grep --no-filename "0xac1cc07a" ./FILES/ubuntlive1mb_?????_201509*.txt >>./0xAC1CC07A.txt

O más precisamente:

find ./FILES -name "ubuntlive1mb_?????_201509*.txt" -type f -print0 | xargs -0 grep --no-filename "0xac1cc07a" $1 >>./0xAC1CC07A.txt

No soy bueno en PowerShell ni en *nix, todos los scripts anteriores se crearon buscando en Google y copiando y pegando.

El sistema de archivos de Windows Box ha sido optimizado al deshabilitar los nombres de archivos DOS y la actualización de directorios en la lista. Ubuntu acaba de instalarse de fábrica.

Respuesta1

Este script de Powershell muy simple debería hacer lo que estás buscando:

$OutFile = "D:\0xAC1CC07A.txt"
Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt -Recurse | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

Esto simplemente agregará las líneas coincidentes al archivo de texto $OutFile. También puede usar esto para obtener los nombres de los archivos o los números de línea de las líneas coincidentes, utilizando las propiedades Nombre de archivo, Ruta y Número de línea, en lugar de solo la propiedad Línea.

Si desea probar un script que se ejecutará en muchos archivos, pero no quiere esperar a que termine de verificarlos todos, puede usar el cmdlet Select-Object para limitar la cantidad de archivos que verificará.

Ejemplo:

Get-ChildItem -Path D:\FILES\ubuntlive1mb_?????_201509*.txt | Select-Object -First 100 | Foreach-Object { Select-String -Path $_ -Pattern "0xAC1CC07A" } | Foreach-Object { Add-Content -Path $OutFile -Value $_.Line }

Esto ejecutará el script anterior solo en los primeros 100 archivos de texto devueltos por Get-ChildItem.

Respuesta2

Tendrá un resultado ligeramente diferente (pero se puede solucionar si es necesario), pero por lo que he visto, es bastante más rápido simplemente seleccionar la cadena de selección directamente en el archivo en lugar de obtener el contenido del archivo. primero.

Select-String "0xAC1CC07A" -Path $filenm.FullName | Add-Content $OutFile

Solo recuerde verificar primero el resultado antes de agregarlo al archivo para obtenerlo de la manera que desee.

En cuanto a velocidad; Get-ChildItem es notoriamente lento en PowerShell (ya que a PowerShell le gusta buscar objetos en lugar de solo representaciones de texto de objetos) y existen varias soluciones para esto.

Sin embargo, la línea Get-ChildItem en su código se puede optimizar. Por lo que he visto, usar Filter es aproximadamente 3,5 veces más rápido que usar inclusiones/exclusiones en un HDD de 7.2k de consumo normal.

Get-ChildItem -Path "D:\FILES" -Filter "ubuntlive1mb_?????_2015090101*.txt" -Recurse -Force

Si la memoria no me falla, las versiones anteriores de PowerShell tenían algunos problemas con el filtro, como por ejemplo si querías todos los archivos con extensión .htm también recogería los archivos con la extensión .html (como si hubieras filtrado *.htm*y no *.htm), así que tal vez quieras estar atento a eso.

información relacionada