¿Cómo excluir algunos archivos que no coinciden con ciertas extensiones con grep?

8

Quiero generar todas las líneas que contienen la palabra OKrecursivamente desde un directorio. Pero hay algunas extensiones que necesito excluir del resultado:

*~
*.map
*.js except *.debug.js

Lo intenté:

grep -r --exclude={*~,*.map} "OK" /some/dir

Excepto que no sé cómo eliminar del resultado todos esos .jsarchivos que no son de depuración .

Desbordamiento de preguntas
fuente

Respuestas:

7

Simplemente pasaría eso por un segundo greppara eliminarlos:

grep -r --exclude={\*~,\*.map} "OK" bar/ | grep -vP '(?<!debug)\.js'

El -vinvierte el partido, la impresión de líneas que no coinciden con el patrón y la -Ppermite a Perl Compatible Regular Expressions, que dejaron usar lookbehinds negativos . Esta expresión regular particular coincidirá con lo .jsque no está precedido por debugel medio (ya que estamos invirtiendo las coincidencias) de que solo .jsse imprimirán esos archivos.

Sin embargo, como @QuestionOverflow señaló en los comentarios, eso podría tener el efecto secundario no deseado de filtrar las líneas que contienen OKy jsdado que grep -vse aplica a toda la salida, no solo al nombre del archivo. Para evitar eso, solo agregue dos puntos (eso es lo que se grepusa para separar los nombres de archivo del contenido del archivo):

grep -r --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js:'

Eso seguirá fallando si su línea de entrada contiene foo.js:o si su nombre de archivo contiene :. Entonces, para estar seguro, use un enfoque diferente:

grep -Tr --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js\t'

Las -Tcausas greppara imprimir una pestaña entre el nombre del archivo y el contenido del archivo. Entonces, si simplemente agregamos un \tal final de la expresión regular, solo coincidirá con los nombres de los archivos, y no con el contenido de la línea.

Aún así, el usofind podría tener más sentido independientemente.

terdon
fuente
1
¿Estaría excluyendo inadvertidamente líneas en los archivos que quiero, pero que contienen ambas OKy .jsen la misma línea?
Desbordamiento de preguntas
@QuestionOverflow ah, sí, de hecho, buena captura. Ver respuesta actualizada.
terdon
Fantástica respuesta. Tengo que aceptar el tuyo ya que pido específicamente grep. Gracias.
Desbordamiento de preguntas
@QuestionOverflow, de nada. En general, sin embargo, findes probablemente mejor para este tipo de cosas. Conseguir lo correcto greppuede ser complicado como lo señaló :).
terdon
Sus soluciones fallan si uno tiene la failglobopción establecida en el shell: bash: no match: --exclude=*~ debe citar sus argumentos de patrón GLOB --excludepara ocultarlos de la expansión del shell, por ejemplo--exclude={\*~,\*.map}
Ian D. Allen
7

Lo usaría findpara ubicar los archivos y canalizar el resultado a través de xargs:

$ find . -type f \! -name "*~" \
                 \! -name "*.map" \
                 \! \( -name "*.js" -and \! -name "*.debug.js" \) \
         -print0 | xargs -0 grep "OK"

Esto busca todos los archivos que no coinciden con " *~", " *.map" o " *.jspero no *.debug.js".

El uso findpuede buscar fácilmente reglas bastante complejas y este enfoque le evita eliminar accidentalmente los falsos positivos, como podría suceder con el doble grep.

Andreas Wiese
fuente
Buena respuesta también :)
Desbordamiento de pregunta
3
Sí, esta es probablemente la mejor manera, +1. También puede usar en -exec grep OK {} +lugar de xargsy evitar un programa adicional.
terdon
2
@IDAllen no, tenga en cuenta que sugerí que -exec +no -exec \;, que ejecutará la menor cantidad de comandos posible, al igual que xargs.
terdon
4

Con zshusted puede hacer:

setopt extendedglob
grep OK some/dir/**/^(*~|*.map|(^*debug).js)

Siempre que, por supuesto, la lista de argumentos no sea demasiado larga, en cuyo caso siempre puede hacer:

printf '%s\0' some/dir/**/^(*~|*.map|(^*debug).js) | xargs -0 grep OK
Graeme
fuente
Además, puede hacer el último solo zsh: autoload zargsyzargs some/dir/**/^(*~|*.map|(^*debug).js) -- grep OK
don_crissti
2

Si no le importa ver el resultado un poco fuera de servicio (si lo hace, puede ordenarlo):

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir **/*.debug.js

Esto requiere que su shell sea compatible **con el globbing recursivo: zsh lo hace fuera de la caja, bash lo hace después de correr shopt -s globstar, ksh93 lo hace después de correr set -o globstar.

Sin **soporte en el shell, puede usar dos comandos grep:

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir
grep -r --include=*.debug.js "OK" /some/dir
Gilles 'SO- deja de ser malvado'
fuente
Mi shell es compatible **, pero parece haber algo mal con el argumento adicional **/*.debug.js, lo que hace que grep se interprete OKcomo un directorio. ¿Has intentado ejecutarlo?
Desbordamiento de preguntas
@QuestionOverflow Mi error, cambié el orden de los argumentos.
Gilles 'SO- deja de ser malvado'
2

Puedes usar ripgrep. Por defecto, ignora los archivos ocultos y respeta su .gitignorearchivo.

Puede especificar las reglas de inclusión o exclusión utilizando los siguientes parámetros:

-g/ --glob GLOBIncluir o excluir archivos y directorios para búsquedas que coincidan con el glob dado.

-t/ --type TYPESolo busca archivos que coincidan con TYPE. Se pueden proporcionar banderas de tipo múltiple.

-T/ --type-not TYPENo busque archivos que coincidan con TYPE.

Use la --type-listbandera para enumerar todos los tipos disponibles.

Aquí hay algunos ejemplos simples:

rg -Tjs "OK"                              # Excludes *.js, *.jsx, *.vue files.
rg -tpy "OK"                              # Includes Python files.
rg --type-add 'map:*.map' -tmap PATTERN   # Excludes *.map files.
rg -g '!*.js' -g '*.debug.js' PATTERN     # Excludes *.js apart of *.debug.js.

Aquí está la solución completa para excluir *.~, *.map, *.js, pero no *.debug.js:

rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' "OK"

Pruebas:

$ touch file.~ file.map file.js file.debug.js file.txt file.md
$ rg --files
file.debug.js
file.js
file.map
file.md
file.txt
$ rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' --files
file.debug.js
file.md
file.txt
kenorb
fuente