¿Cómo encuentro archivos que no contienen un patrón de cadena dado?

536

¿Cómo encuentro los archivos en el directorio actual que no contienen la palabra foo(usando grep)?

Senthil Kumar
fuente

Respuestas:

818

Si su grep tiene la opción -L(o --files-without-match):

$ grep -L "foo" *
ghostdog74
fuente
1
Como se señaló en otra parte, ack ayuda a evitar archivos .svn (subversión) de manera predeterminada.
GuruM
11
@GuruM Esto se puede hacer en GNU grep exportando la variable GREP_OPTIONS='--exclude-dir=.svn --exclude-dir=.git': ^)
bufh
66
O el equivalente usando ag :ag -L 'foo'
obispo
55
Funciona como magia! Sugerencia: use en -rLlugar de -Lhacer coincidir subdirectorios
Ufos
1
@Larry: una forma más limpia de evitar problemas de bloqueo es utilizar la opción larga "vacía" de esta manera: grep -L 'foo' -- *el estándar es que los comandos que toman opciones largas se usan --para indicar que no hay más opciones después de este punto.
Paddy Landau
45

Echa un vistazo a ack. Hace la .svnexclusión automáticamente, le da expresiones regulares de Perl y es una simple descarga de un solo programa de Perl.

El equivalente de lo que está buscando debe ser, en ack:

ack -L foo
Andy Lester
fuente
24

Puedes hacerlo solo con grep (sin encontrar).

grep -riL "foo" .

Esta es la explicación de los parámetros utilizados en grep

     -L, --files-without-match
             each file processed.
     -R, -r, --recursive
             Recursively search subdirectories listed.

     -i, --ignore-case
             Perform case insensitive matching.

Si usa l(en minúsculas) obtendrá lo contrario (archivos con coincidencias)

     -l, --files-with-matches
             Only the names of files containing selected lines are written
Adrian
fuente
17

El siguiente comando me da todos los archivos que no contienen el patrón foo:

find .  -not  -ipath '.*svn*' -exec  grep  -H -E -o -c  "foo"  {} \; | grep 0
Senthil Kumar
fuente
44
Desea cambiar el grep 0 al final por grep 0 $ (de lo contrario, obtendrá coincidencias erróneas en los archivos que tienen el carácter 0 en su nombre de archivo).
clouseau
99
@clouseau tiene razón en su mayoría ... ¡Sin embargo, grep '0$'también coincidiría con archivos con múltiplos de 10 líneas! Debe grep ':0$'al final verificar un ': 0' explícito al final de la línea. Entonces solo obtendrá archivos con cero líneas coincidentes.
TrinitronX
El UNIX en el que estoy no tenía versiones de find o grep con estas opciones, así que tuve que ir con el comando "ack" sugerido en otros comentarios.
KC Baltz
14

El siguiente comando excluye la necesidad de encontrar para filtrar las svncarpetas mediante el uso de un segundo grep.

grep -rL "foo" ./* | grep -v "\.svn"
user999305
fuente
9

Realmente necesitarás:

find .  -not  -ipath '.*svn*' -exec  grep  -H -E -o -c  "foo"  {} \; | grep :0\$
Forrest Tiffany
fuente
6

Tuve buena suerte con

grep -H -E -o -c "foo" */*/*.ext | grep ext:0

Mis intentos con grep -vsolo me dieron todas las líneas sin "foo".

Johnny
fuente
4

Problema

Necesito refactorizar un proyecto grande que usa .phtmlarchivos para escribir HTML usando código PHP en línea. Quiero usar plantillas de bigote en su lugar. Quiero encontrar cualquier .phtmlgiles que no contenga la cadena, new Mustacheya que aún deben reescribirse.

Solución

find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$ | sed 's/..$//'

Explicación

Antes de las tuberías:

Encontrar

find . Encuentra archivos recursivamente, comenzando en este directorio

-iname '*.phtml'El nombre del archivo debe contener .phtml( iesto hace que no distinga entre mayúsculas y minúsculas)

-exec 'grep -H -E -o -c 'new Mustache' {}'Ejecute el grepcomando en cada una de las rutas coincidentes

Grep

-H Siempre imprima encabezados de nombre de archivo con líneas de salida.

-E Interprete el patrón como una expresión regular extendida (es decir, forzar a grep a comportarse como egrep).

-o Imprime solo la parte coincidente de las líneas.

-c Solo un recuento de líneas seleccionadas se escribe en la salida estándar.


Esto me dará una lista de todas las rutas de archivos que terminan en .phtml, con un recuento de la cantidad de veces que la cadena new Mustacheocurre en cada una de ellas.

$> find . -iname '*.phtml$' -exec 'grep -H -E -o -c 'new Mustache' {}'\;

./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml:0
./app/MyApp/Customer/View/Account/studio.phtml:0
./app/MyApp/Customer/View/Account/orders.phtml:1
./app/MyApp/Customer/View/Account/banking.phtml:1
./app/MyApp/Customer/View/Account/applycomplete.phtml:1
./app/MyApp/Customer/View/Account/catalogue.phtml:1
./app/MyApp/Customer/View/Account/classadd.phtml:0
./app/MyApp/Customer/View/Account/orders-trade.phtml:0

La primera tubería grep :0$filtra esta lista para incluir solo líneas que terminan en :0:

$> find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$

./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml:0
./app/MyApp/Customer/View/Account/studio.phtml:0
./app/MyApp/Customer/View/Account/classadd.phtml:0
./app/MyApp/Customer/View/Account/orders-trade.phtml:0

La segunda tubería sed 's/..$//'elimina los dos caracteres finales de cada línea, dejando solo las rutas de archivo.

$> find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$ | sed 's/..$//'

./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml
./app/MyApp/Customer/View/Account/studio.phtml
./app/MyApp/Customer/View/Account/classadd.phtml
./app/MyApp/Customer/View/Account/orders-trade.phtml
Gruñón
fuente
3

Si está utilizando git, esto busca todos los archivos rastreados:

git grep -L "foo"

y puede buscar en un subconjunto de archivos rastreados si tiene activado ** globbing del subdirectorio ( shopt -s globstaren .bashrc, vea esto ):

git grep -L "foo" -- **/*.cpp
Zak
fuente
1

Mi grep no tiene ninguna opción -L. Encuentro la solución para lograr esto.

Las ideas son:

  1. para volcar todo el nombre del archivo que contiene la cadena merecida en un txt1.txt.
  2. volcar todo el nombre del archivo en el directorio a un txt2.txt.
  3. marque la diferencia entre el archivo de volcado 2 con el comando diff.

    grep 'foo' *.log | cut -c1-14 | uniq > txt1.txt
    grep * *.log | cut -c1-14 | uniq > txt2.txt
    diff txt1.txt txt2.txt | grep ">"
    
usuario6305682
fuente
Olvidé los comandos, pero en lugar de descartar los nombres de los archivos, en realidad puede hacer una diffsecuencia de salida entre dos (creo que rodea los comandos con paréntesis, y también hay un paréntesis angular en alguna parte), si su sistema lo admite, lo que supongo es la pregunta, ya que no es compatiblegrep -L
Dexygen
1

find *20161109* -mtime -2|grep -vwE "(TRIGGER)"

Puede especificar el filtro en "buscar" y la cadena de exclusión en "grep -vwE". Use mtime en find si necesita filtrar también en el tiempo modificado.

zandeep
fuente
Esto parece mostrarme todas las líneas sin la cadena, el OP solicita solo los nombres de archivo.
Ben Farmer
1

Informe de error abierto

Como comentó @tukan, hay un informe de error abierto para Ag con respecto a la bandera -L/ --files-without-matches:

Como hay poco progreso en el informe de error, no se debe confiar en la -Lopción mencionada a continuación , siempre y cuando el error no se haya resuelto. Utilice diferentes enfoques presentados en este hilo en su lugar. Citando un comentario para el informe de error [énfasis mío]:

¿Alguna actualización sobre esto? -Lignora completamente las coincidencias en la primera línea del archivo. Parece que si esto no se va a solucionar pronto, la bandera debería eliminarse por completo, ya que efectivamente no funciona como se anuncia .


The Silver Searcher - Ag (función prevista - ver informe de error)

Como una alternativa poderosa a grep, podría usar The Silver Searcher - Ag :

Una herramienta de búsqueda de código similar a ack, con un enfoque en la velocidad.

Mirando man ag, encontramos la opción -Lu --files-without-matches:

...

OPTIONS
    ...

    -L --files-without-matches
           Only print the names of files that don´t contain matches.

Es decir, para buscar de forma recursiva archivos que no coinciden foo, desde el directorio actual:

ag -L foo

Para buscar solo en el directorio actual archivos que no coincidan foo, simplemente especifique --depth=0la recursividad:

ag -L foo --depth 0
dfri
fuente
Esto falla de vez en cuando debido al -Lerror - github.com/ggreer/the_silver_searcher/issues/238
tukan
@tukan gracias por el aviso. He actualizado la respuesta; eligiendo no eliminar la respuesta, sino abriendo con la información sobre el error.
dfri
1

Otra alternativa cuando grep no tiene la opción -L (IBM AIX por ejemplo), con nada más que grep y el shell:

for file in * ; do grep -q 'my_pattern' $file || echo $file ; done
JMD
fuente
-4
grep -irnw "filepath" -ve "pattern"

o

grep -ve "pattern" < file

el comando anterior nos dará el resultado cuando -v encuentre el inverso del patrón que se está buscando

Arrendajo
fuente
1
Esto imprime las líneas que no contienen el patrón. Puede agregar la -lopción para imprimir solo el nombre del archivo; pero esto todavía imprime los nombres de cualquier archivo que contenga cualquier línea que no contenga el patrón. Creo que el OP quiere encontrar los archivos que no contienen ninguna línea que contenga el patrón.
tripleee
El comando que proporcionó enumera los archivos en "filepath" con todas sus líneas que no contienen "patrón".
Aprodan
-6

El siguiente comando podría ayudarlo a filtrar las líneas que incluyen la subcadena "foo".

cat file | grep -v "foo"
walkerlin
fuente
2
Esto imprime líneas que no coinciden, no nombres de archivos que no contienen una coincidencia en ninguna línea. Para agregar insulto a la lesión, es un uso inútil decat .
tripleee