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

Quiero excluir el file ./test/main.cpp de 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 get el resultado que quiero usando múltiples commands en una disposition de tuberías y filters, pero ¿hay alguna cita / escape que haga que grep comprenda lo que quiero de forma nativa?

grep no puede hacer esto para files en un directory determinado si tiene más files con el mismo nombre en directorys diferentes, use find en su lugar:

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

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 los files ocultos, igual de bien para excluir .git, .svn …).

Podría escribir un libro: "El arte perdido de xargs ". El find ... -exec … '; lanza un grep para cada file (pero la variante con -exec … + no). Bueno, estamos perdiendo ciclos de CPU en estos días, ¿por qué no, verdad? Pero si el performance, la memory y la potencia son un problema: use xargs:

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

GNU's find 's -print0 NUL -print0 su salida y la opción xargs ' -0 respeta ese formatting como input. Esto garantiza los divertidos personajes que tenga su file, la canalización no se confundirá. La opción -r se asegura de que no haya ningún error en caso de que find no encuentre nada.

Tenga en count que ahora puede hacer cosas como:

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

GNU grep's -z hace lo mismo que xargs ' -0 .

Si su find compatible con la -path que se agregó a POSIX en 2008 pero aún falta en Solaris:

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

Para el logging, este es el enfoque que prefiero:

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

Al mantener el grep al principio del command, creo que esto es un poco más claro, además de que no deshabilita el resaltado de color de grep . En cierto sentido, el uso de find en una sustitución de command es solo una forma de extender / replace el subset (limitado) de búsqueda de files de la funcionalidad de grep .


Para mí, la syntax find -exec es algo arcana. Una complejidad con find -exec es la (a veces) necesidad de escaping de varios caracteres (especialmente si \; se usa bajo Bash). Solo con el propósito de poner las cosas en contexts familiares, los siguientes dos commands 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 subdirectorys , puede ser necesario utilizar un comodín. No entiendo completamente el esquema aquí – habla sobre arcanas :

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

Una nota adicional para generalizar las soluciones basadas en el find para su uso en scripts : La línea de command grep debe include la opción -H / --with-filename . De lo contrario, cambiará el formatting de salida bajo la circunstancia de que solo haya un nombre de file en los resultados de búsqueda de find . Esto es notable porque no parece ser necesario si se usa la búsqueda de files nativa de grep (con la opción -r ).

… Aún mejor, sin embargo, es include /dev/null como primer file de búsqueda. Esto resuelve dos problemas:

  • Asegura que si hay un file para search, grep pensará que hay dos y usará el modo de salida de múltiples files.
  • Asegura que si no hay files para search, grep pensará que hay un file y no esperará en stdin.

Entonces la respuesta final es:

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