Ejecute `grep` excluyendo un archivo en una ruta específica

12

Quiero excluir el archivo ./test/main.cppde mi búsqueda.

Esto es lo que estoy viendo:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Sé que es posible obtener el resultado que quiero mediante el uso de múltiples comandos en una disposición de tuberías y filtros, pero ¿hay alguna cita / escape que haga grepcomprender lo que quiero de forma nativa?

sin bar
fuente
Una solución basada en el filtrado de la salida no se escala bien porque busca innecesariamente el archivo antes de excluir los resultados asociados. El problema se magnifica si quiero excluir directorios completos (con --exclude-dir). Es por eso que me gustaría hacer que grep realice la exclusión de forma nativa.
nobar
1
--exclude especifica glob no un camino
PersianGulf

Respuestas:

6

grep no puede hacer esto para el archivo en un directorio determinado si tiene más archivos con el mismo nombre en diferentes directorios, use find en su lugar:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+

MichalH
fuente
¿Por qué escapas \!y \+? Parece funcionar bien sin las barras invertidas.
nobar
@nobar Estoy acostumbrado porque algunos caracteres son palabras clave de shell, por lo que nunca te sorprenderás porque no puede pasar nada si se escapan.
MichalH
" grepno se puede hacer esto, use finden su lugar" - perfecto.
nobar
4

No creo que sea posible con GNU grep. Sin embargo, no necesitas tuberías.

Con find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

Con zsh:

grep pattern ./**/*~./test/main.cpp(.)

(excluye archivos ocultos, así como excluir .git, .svn ...).

Stéphane Chazelas
fuente
2

Podría escribir un libro: "El arte perdido de xargs". Los find ... -exec … ';lanzamientos de un grep para cada archivo (pero con la variante de -exec … +que no lo hace). Bueno, estamos desperdiciando ciclos de CPU en estos días, así que ¿por qué no? Pero si el rendimiento, la memoria y la potencia son un problema: use xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

De GNU find's -print0se NULtermine los su producción y xargs' -0honores opción ese formato como entrada. Esto garantiza cualquier personaje divertido que tenga su archivo, la tubería no se confundirá. La -ropción asegura que no haya errores en caso de que findno encuentre nada.

Tenga en cuenta que ahora puede hacer cosas como:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep's -zhace lo mismo que xargs ' -0.

Oteo
fuente
3
Algunas notas interesantes, pero no estoy seguro de que tenga razón sobre el problema de rendimiento. Según tengo entendido, find -exec (cmd) {} +funciona igual xargsy find -exec (cmd) {} \;funciona igual que xargs -n1. En otras palabras, su declaración solo es correcta si \;se utiliza la versión.
nobar
3
La canalización xargses menos eficiente que el uso -exec … +(aunque marginalmente). Ninguna de las respuestas aquí mencionan -exec … \;.
Gilles 'SO- deja de ser malvado'
1
Bueno, s - t. Salgo conmigo mismo. Gracias por los comentarios y correcciones. Pensé que el \ + era un error tipográfico. Oh, mira, -exec ... +agregado en enero de 2005. Sí, no estoy desactualizado ... en absoluto.
Oteo
2

Si su findsoporte -pathfue agregado a POSIX en 2008 pero aún falta en Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +
Cuonglm
fuente
1
No creo que eso funcione porque nobar quiere main.cpp en otros directorios
Eric Renouf
1
¿no excluirá su patrón main.cpp de todos los demás directorios también? Eso no sería deseable
Eric Renouf
@EricRenouf: Oh, mi error, una lectura errónea. Actualicé mi respuesta.
Cuonglm
@Gilles: ¿Por qué -pathno es POSIX?
Cuonglm
Ah, lo siento, mi error, se agregó en 2008. Sin embargo, todavía falta en Solaris.
Gilles 'SO- deja de ser malvado'
1

Para el registro, aquí está el enfoque que prefiero:

grep pattern $(find . -type f ! -path './test/main.cpp')

Al mantener el grepprincipio del comando, creo que esto es un poco más claro, además no deshabilita grepel resaltado de color. En cierto sentido, usar finduna sustitución de comandos es solo una forma de extender / reemplazar el subconjunto (limitado) de búsqueda de archivos de grepla funcionalidad de.


Para mí, la find -execsintaxis es algo arcana. Una complejidad con find -execes la (a veces) necesidad de escapar de varios caracteres (especialmente si \;se usa en Bash). Solo para poner las cosas en contextos familiares, los siguientes dos comandos son básicamente equivalentes:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Si desea excluir subdirectorios , puede ser necesario usar un comodín. No entiendo completamente el esquema aquí, habla sobre lo arcano :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Una nota más para generalizar findsoluciones basadas en el uso en scripts : grepla línea de comandos debe incluir la opción -H/ --with-filename. De lo contrario, cambiará el formato de salida bajo la circunstancia de que solo haya un nombre de archivo en los resultados de búsqueda find. Esto es notable porque no parece ser necesario si se usa grepla búsqueda de archivos nativa (con la -ropción).

... Aún mejor, sin embargo, es incluir /dev/nullcomo primer archivo para buscar. Esto resuelve dos problemas:

  • Asegura que si hay un archivo para buscar, greppensará que hay dos y usará el modo de salida de múltiples archivos.
  • Asegura que si no hay archivos para buscar, greppensará que hay un archivo y no se quedará esperando en stdin.

Entonces la respuesta final es:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')
sin bar
fuente
No debe usar la salida de finden una sustitución de comando. Esto se rompe si hay nombres de archivos que contienen espacios u otros caracteres especiales. Uso find -exec, es robusto y fácil de usar.
Gilles 'SO- deja de ser malvado'
@Gilles: Muy buen punto: también la salida podría exceder los límites de tamaño de la línea de comandos de algunos programas. Advertencia emptor.
nobar
Ugh La sintaxis 'encontrar' es terriblemente difícil. '-o' es un operador "o" (también '-or' en Linux), pero su uso típico (por ejemplo, con '-prune') no se asigna conceptualmente a la noción de un lógico o. Es funcional o más que lógico o.
nobar
Otra manera de excluir subdirectorios basados en la coincidencia de un nombre: find -iname "*target*" -or -name 'exclude' -prune. Bueno, de alguna manera funciona: el directorio podado se enumerará, pero no se buscará. Si no desea que ! -name 'exclude'
aparezca