Soy nuevo en el ecosistema de Windows. Me han encomendado la tarea de escribir un programa que buscará en varias decenas (tal vez incluso cientos) de miles de archivos una cadena en particular. La cadena que debe coincidir es un número de serie que consta únicamente de números y letras y tiene menos de 20 caracteres. En este momento, mi programa está ejecutando el siguiente comando:
findstr /i /m /s "searchStr" "C:\Directory\To\Search\*.*"
El comando anterior funciona, sin embargo, es demasiado lento. Los archivos que puedan contener un número de serie particular solo tendrán el número de serie en la primera línea.
¿Alguien conoce una forma eficiente de buscar recursivamente en un directorio todos los archivos que contienen una cadena particular solo en la primera línea?
Respuesta1
En PowerShell (v3.0+), tal vez...
Get-ChildItem -Path x:\pathto\*.log `
| ForEach-Object {
if (Get-Content -LiteralPath $_ -First 1 `
| Select-String -SimpleMatch -Pattern 'serialnumber')
{
Write-Output $_
}
}
Diferentes parámetros para Get-ChildItem
poder recurrir a subcarpetas, etc.; poder Get-Content
obtener más o menos contenido del archivo; y Select-String
puede realizar coincidencias más complejas (expresiones regulares, distinción entre mayúsculas y minúsculas, etc.).
Respuesta2
Puedo sugerir algunas opciones si no necesita usar findstr
, pero antes que nada debería ver si puede restringir la búsqueda a archivos de un determinado tipo, ya que eso seguramente acelerará las cosas.
Localizador de archivos LiteEn mi experiencia, es más rápido para encontrar archivos y verificar su contenido. Asegúrese de completar los campos "nombre de archivo" (si corresponde) y "texto contenido", así como el directorio de inicio.
ag -il "searchStr"
:agestá diseñado para la velocidad, por lo que debería brindarle resultados rápidamente. Asegúrese de restringir la búsqueda por tipo de archivo si puede, aunque los archivos binarios ya se omiten de forma predeterminada. También disponible bajoCygwin.find -exec awk 'BEGIN {IGNORECASE=1} NR==1 && /searchStr/ {print FILENAME": "$0}' {} \;
Pruebe esto si tiene Cygwin u otro entorno similar a POSIX disponible, para comprobar su idea de buscar solo la primera línea. Combinefind
para obtener los nombres de los archivos (y con suerte también filtrarlos) yawk
verifique la primera línea e imprímala junto con el nombre del archivo.find | parallel 'perl -lane '\'' print "$ARGV: $_" if $. == 1 and /searchStr/i '\'' {}'
Otra idea para intentar acelerar las cosas es poner a trabajar los núcleos e hilos disponibles: eso es lo queparalelo GNUes para. Este ejemplo tiene deportesperl
, pero hace lo mismo queawk
el3.
anterior. Aquí hay un desglose del comando:find
busque archivos en el directorio actual y sus subdirectorios. Puede especificar un directorio diferente para buscar y un patrón de archivo o extensión para filtrar:find /cygdrive/c/Directory/To/Search -iname "*.txt"
.|
"tubería", es decir, alimenta la lista de resultados al siguiente comando.parallel
ejecute el siguiente comando en paralelo.perl
Lenguaje de scripting que sobresale en la manipulación de archivos de texto, puede reemplazarsed
oawk
.-lane
útil conjunto de interruptores para perl one-liners.'\''
Apóstrofe escapado, necesario ya que ya abrimos un apóstrofe establecido despuésparallel
.print "$ARGV: $_"
imprime el nombre del archivo ($ARGV
), dos puntos, un espacio y la línea completa ($_
).if
Sólo ejecute la instrucción anterior si se cumplen las siguientes condiciones.$. == 1
El número de línea ($.
) es igual a uno (1
), es decir, estamos viendo la primera línea del archivo.and
También se debe cumplir la siguiente condición./searchStr/i
la línea que se examina contiene el textosearchStr
, sin distinguir entre mayúsculas y minúsculas.'\''
otro apóstrofe escapado marca el final de laperl
instrucción.{}
esto será sustituido porparallel
cada uno de los nombres de archivo pasados porfind
.'
final de laparallel
instrucción.
Actualizar:Ambos awk
y perl
leen el archivo completo incluso si las acciones están vinculadas solo a la primera línea. La solución es detener explícitamente la elaboración en la línea 2:
find -exec awk 'BEGIN {IGNORECASE=1} NR > 1 {exit} /searchStr/ {print FILENAME": "$0}' {} \;
find | parallel 'perl -lape '\'' exit if $. == 2; print "$ARGV: $_" if /searchStr/i '\'' {}'