¿Cómo utilizar de forma segura la salida de grep en un script?

¿Cómo utilizar de forma segura la salida de grep en un script?

En un script, quiero buscar archivos que contengan texto. Necesito saber el archivo en el que se encuentra el texto y la línea completa dentro del archivo en el que se encuentra el texto. grepEs la utilidad que hace esto, pero ¿cómo puedo obtener el resultado en un formato utilizable, dado que puede haber :en nombres de archivos? ¿Existe algún tipo de --porcelainmodo grepque pueda usar, algo así como gitlos comandos que suelen tener?

Ejemplo: tengo una carpeta llena de archivos con nombres así test-num:1:date:jan-2que quiero revisar. Los archivos contienen FAILURE:<some reason>o SUCCESS:<some reason>(entre otras cosas). Necesito un script que busque ciertos motivos y almacene el nombre del archivo y el motivo (toda la línea de texto está bien) para su posterior procesamiento. La salida puede estar en cualquier tipo de estructura de datos, siempre que pueda ejecutar código sobre ella.

Respuesta1

No existe tal cosa como un grep --porcelain, el manejo de caracteres especiales en los nombres de archivos siempre ha sido una idea de último momento en UNIX. Podrías probar algo como esto, al precio de la eficiencia:

pattern='some pattern'
for file in ./*; do
    grep -- "$pattern" "$file" | while read -r line; do
        printf 'file: %s, line: %s\n' "$file" "$line"
    done
done

Respuesta2

Las versiones recientes (-ish) de GNU grep tienen una opción -Zque hace que el resultado no sea ambiguo, pero está dirigido principalmente a usos como grep -lZ … | xargs -0. Todavía funciona si enumeras el contenido de una línea, el byte nulo reemplaza los dos puntos y el contenido de la línea aún termina en una nueva línea¹, pero los shells no son buenos para manejar bytes nulos, por lo que tendrás dificultades para analizar este resultado. .

Una solución sencilla (con una ligera penalización en el rendimiento) es ejecutar grep en cada archivo individualmente.

Otra solución es utilizar un lenguaje como Perl o Python. Perl es bastante bueno emulando grep;  grep REGEXes básicamente perl -ne '/REGEXP/ and print'.

Pero es posible que no necesite esto en absoluto si el resultado no es realmente ambiguo. Por ejemplo, si las líneas coincidentes no contienen dos puntos, entonces el nombre del archivo es todo lo que está en una línea hasta los últimos dos puntos. Si todas las líneas coincidentes comienzan con SUCCESSo FAILUREy estas palabras no aparecen en los nombres de los archivos, entonces puede usar esto para localizar la separación, etc.

¹ Excepto cuando se utiliza -zpara filtrar registros terminados en nulo en lugar de registros terminados en nueva línea, entonces nulo es tanto el terminador de nombre de archivo como el terminador de resultado; sin -ola salida sigue siendo inequívoco, con registros de salida alternos que son nombres de archivos y registros coincidentes en la salida.

Respuesta3

Cómo utilizar de forma segura la salida de grepen un guión?

... La salida puede ser en cualquier tipo deestructura de datos,siempre que pueda ejecutar código sobre él.

Los scripts de Shell realmente no tienen estructuras de datos. Hay matrices, pero eso es todo, y no es fácil canalizar la salida a una matriz de forma segura. (Nombres de archivospodercontener nuevas líneas.)

La mejor manera deEjecutar códigosobre sus archivos en un script de shell es simplemente ejecutar el código sobre los archivos, no intentar guardar los nombres de los archivos para su uso posterior.

Para hacer esto, use find:

find somedir -type f -exec grep -q somepattern {} \; -exec somecommand {} \;

Sin embargo, al leer su pregunta más detenidamente, parece que en realidad no deseaEjecutar códigosobre sus archivos, solo desea procesar un poco el texto en ciertas líneas. En este caso, la opción GNU Grep -zprobablemente sea lo que desea. Eso, y un conocimiento de Sed o Awk, resolverán su pregunta.


Podría ser inteligente cambiar la convención de nomenclatura de archivos.

información relacionada