¿Cómo realizo xargs grep en la salida grep que tiene espacios?

8

Estoy buscando archivos basados ​​en una expresión regular, y luego intento buscar contenido en esos archivos. Entonces, por ejemplo, tengo algo como

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp" | grep "<name regex>" | xargs grep "<content regex>"

El problema con el que me encuentro es que algunos de los caminos tienen espacios en ellos, lo que confunde xargs. Sé que si solo estuviera usando find, podría usar el -print0argumento (junto con el -0argumento encendido xargs) para evitar que los xargs traten los espacios como delimitadores. ¿Hay algo similar con grep?

¿O estoy abordando este problema por completo de manera incorrecta? Ingenuamente, finda grepque xargs greptiene sentido para mí, pero estoy abierto a otros enfoques que permitan obtener los mismos resultados.

cuantícula
fuente
2
puede colocar argumentos con el xargsuso del -iparámetro, a la cat sample.txt | grep "pat t ern" | xargs -i grep "{}"- las llaves indican dónde colocar el argumento. El manual me dice que -iestá en desuso, por -Ilo que quizás valga la pena echarle un vistazo también.
dougBTV

Respuestas:

5

Use algo como esto quizás (si gnu grep).

grep -r 'content pattern' --include==*.cpp

hombre grep

--include = GLOB Buscar solo archivos cuyo nombre base coincida con GLOB (utilizando la coincidencia con comodines como se describe en --exclude)

Consulte también las opciones para delimitadores nulos.

-Z, --null Genera un byte cero (el carácter ASCII NUL) en lugar del carácter que normalmente sigue al nombre de un archivo. Por ejemplo, grep -lZ genera un byte cero después de cada nombre de archivo en lugar de la nueva línea habitual. Esta opción hace que la salida sea inequívoca, incluso en presencia de nombres de archivo que contienen caracteres inusuales como líneas nuevas. Esta opción se puede usar con comandos como find -print0, perl -0, sort -z y xargs -0 para procesar nombres de archivos arbitrarios, incluso aquellos que contienen caracteres de nueva línea.

-z, --null-data Trata la entrada como un conjunto de líneas, cada una terminada por un byte cero (el carácter ASCII NUL) en lugar de una nueva línea. Al igual que la opción -Z o --null, esta opción se puede usar con comandos como sort -z para procesar nombres de archivos arbitrarios.

Zoredache
fuente
Tenga en cuenta que grep -r include='*.cpp'es un globo de shell, y también está alineado con las funciones w / find . -name '*.cpp' -exec grep -e 'content_pattern' -- {} \;not w /find . -name '*.cpp' | grep 'name_pattern' | xargs grep 'content_pattern'
mikeserv
4

Si tienes que saltar muchos aros, la eficiencia de los xargs se pierde de todos modos. Aquí hay una solución cruda:

find . -iname "*.cpp" | grep "<pattern>" | while read -r x; do grep exa "$x"; done

Cada vez que me encuentro con problemas con espacios en los nombres de archivo, la respuesta son comillas dobles en una variable.

Baazigar
fuente
Esto ejecuta el grep interno del bucle únicamente para cada línea encontrada por el grep externo. Eso es un montón de gastos generales.
Adam Katz
3

Use findpara hacer todo el filtrado de nombres de archivo. Más bien que

find . -name "*.cpp" | grep "foo" | xargs grep 

hacer

find . -name "*.cpp" -name "*foo*" -print0 | xargs -0 grep 

Si quieres hacer algo un poco más complicado, como

find . -name "*.cpp" | egrep "foo|bar" | xargs grep 

tu puedes hacer

find . -name "*.cpp" "(" -name "*foo*" -o -name "*bar*" ")" -print0 | xargs -0 grep 

Tenga en cuenta que estos deberían funcionar incluso para archivos con nuevas líneas en sus nombres.

Y, si necesita el poder de expresiones regulares completas, puede usar -regex.

Scott
fuente
2

Esto debería funcionar incluso sin las herramientas GNU:

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp"  | grep "<name regex>" | perl -pe 's/\n/\0/' \
  | xargs -0 grep "<content regex>"

La perlllamada reemplaza los saltos de línea con caracteres nulos, lo que permitirá xargs -0interpretar la entrada por línea en lugar de por espacio en blanco.

Con GNU, puede eliminar la perlllamada y cambiar xargs -0 …axargs -d "\n" …

¿No tienes perlo GNU? Intenta en su awk '{printf "%s%c", $0, 0}'lugar.

Adam Katz
fuente
1
Esto puede no hacer lo correcto si algunos de los nombres de archivo incluyen nuevas líneas (una ocurrencia bastante inusual, por supuesto, pero no imposible).
Dhag
@dhag tiene un punto válido con respecto xargs -d "\n". Es un hecho muy inusual, pero si no tiene control de los datos y le preocupa que sea un riesgo de seguridad, tenga cuidado con las expectativas de salida.
Adam Katz