Cómo procesar cada línea recibida como resultado del comando grep

152

Tengo varias líneas recuperadas de un archivo después de ejecutar el comando grep de la siguiente manera:

var=`grep xyz abc.txt`

Digamos que obtuve 10 líneas que consisten en xyz como resultado.

Ahora necesito procesar cada línea que obtuve como resultado del comando grep. ¿Cómo procedo para esto?

XYZ_Linux
fuente
66
Ninguna de las respuestas aquí menciona el poder de grep -oeste tipo de cosas. La -obandera devolverá solo el texto que coincida, con una coincidencia por línea de salida. (No es exhaustivo, por lo que echo aaa |grep 'a*'solo te da "aaa" y omite las tres coincidencias parciales "", "a" y "aa")
Adam Katz

Respuestas:

269

Una de las formas fáciles es no almacenar la salida en una variable, sino iterar directamente sobre ella con un ciclo while / read.

Algo como:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

Hay variaciones en este esquema dependiendo de exactamente lo que buscas.

Si necesita cambiar las variables dentro del ciclo (y hacer que ese cambio sea visible fuera de él), puede usar la sustitución del proceso como se indica en la respuesta de fedorqui :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)
Estera
fuente
si no hay línea con xyz?
XYZ_Linux
Entonces no pasa nada, el bucle no se ejecuta.
Mat
13
El problema con este método es que (debido a la tubería) todo dentro del bucle está en una subshell, por lo que establecer variables definidas fuera del bucle durante el bucle no hace que sus valores estén disponibles después del bucle.
David Doria el
3
@David: proporcionó una alternativa para abordar su inquietud. (Fedorqui ya lo había abordado también.)
Mat
Para los comandos cuya última línea de salida no termina con una nueva línea, necesitaría: while read p || [[ -n $p ]]; do ...(tomado de stackoverflow.com/questions/1521462/… )
Ohad Schneider
21

Puede hacer el siguiente while readbucle, que se alimentará del resultado del grepcomando utilizando la llamada sustitución de proceso:

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

De esta manera, no tiene que almacenar el resultado en una variable, sino "inyectar" directamente su salida al bucle.


Tenga en cuenta el uso IFS=y de read -racuerdo con las recomendaciones de BashFAQ / 001: ¿Cómo puedo leer un archivo (flujo de datos, variable) línea por línea (y / o campo por campo)? :

La opción -r para leer evita la interpretación de la barra invertida (generalmente se usa como un par de línea nueva de barra invertida, para continuar en varias líneas o para escapar de los delimitadores). Sin esta opción, cualquier barra invertida sin escape en la entrada será descartada. Casi siempre debe usar la opción -r con lectura.

En el escenario anterior, IFS = evita el recorte de los espacios en blanco iniciales y finales. Elimínalo si quieres este efecto.

En cuanto a la sustitución del proceso, se explica en la página de hackers de bash :

La sustitución de procesos es una forma de redireccionamiento donde la entrada o salida de un proceso (alguna secuencia de comandos) aparece como un archivo temporal.

fedorqui 'así que deja de dañar'
fuente
OK, marqué la forversión. Intenté hacer un bucle "${$(grep xyz abc.txt)[@]}"como en stackoverflow.com/a/14588210/1983854 pero no pude. Así que solo dejo la primera versión.
fedorqui 'SO deja de dañar'
1
No puede aplicar la expansión de parámetros a una sustitución de comando (a menos que esté usando zsh, donde ese tipo de anidamiento probablemente funcione).
chepner
Un posible problema con este idioma es que si algo dentro del bucle intenta leer desde la entrada estándar, obtendrá parte del archivo. Para evitar esta posibilidad, me gusta enviar el archivo a través del descriptor de archivo 3 en lugar de stdin. Solo use while IFS= read -r result <&3ydone 3< <(grep ...
Gordon Davisson
8

Sugeriría usar awk en lugar de grep + algo más aquí.

awk '$0~/xyz/{ //your code goes here}' abc.txt

Julien Grenier
fuente
1
¿Cómo haría referencia a la línea completa encontrada //your code goes here?
user857990
2
@ user857990 Toda la línea se representa como $ 0 en AWK.
Julien Grenier
2

Sin ninguna iteración con la opción grep --line-buffered:

your_command | grep --line-buffered "your search"

Ejemplo de la vida real con una salida del comando de depuración del enrutador Symfony PHP Framework, para seleccionar todas las rutas relacionadas con "api":

php bin/console d:r | grep --line-buffered "api"
Nicolas Bonnici
fuente
La única solución que funciona si your_command es de larga duración (como tail -f some.log, en mi caso) ...
Izkata
0

A menudo, el orden del procesamiento no importa. GNU Parallel está hecho para esta situación:

grep xyz abc.txt | parallel echo do stuff to {}

Si el procesamiento es más como:

grep xyz abc.txt | myprogram_reading_from_stdin

y myprogrames lento, entonces puedes ejecutar:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin
Ole Tange
fuente
0

Iterar sobre los resultados grep con un ciclo while / read. Me gusta:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done
Abhi km
fuente
0

Para aquellos que buscan una línea:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
Laurent
fuente