El uso de xargs en las respuestas anteriores no es necesario; puedes lograr lo mismo así:
find . -type f -exec grep -q <string-to-match> {} \; -not -exec grep -q <string-not-to-match> {} \; -print
grep -q significa ejecutar silenciosamente pero devuelve un código de salida que indica si se encontró una coincidencia; find luego puede usar ese código de salida para determinar si debe seguir ejecutando el resto de sus opciones. Si -exec grep -q <string-to-match> {} \; devuelve 0, luego pasará a ejecutar -not -exec grep -q <string-not-to-match>{} \; . Si eso también devuelve 0, continuará ejecutando -print , que imprime el nombre del archivo.
Como ha señalado otra respuesta, usando find de esta manera tiene grandes ventajas sobre grep -Rl donde solo desea buscar archivos de un tipo determinado. Si, por el contrario, realmente desea buscar en todos los archivos, grep -Rl es probablemente más rápido, ya que usa un grep proceso para realizar el primer filtro para todos los archivos, en lugar de un grep separado proceso para cada archivo.
Prueba esto:
grep -rl <string-to-match> | xargs grep -L <string-not-to-match>
Explicación:grep -lr hace que grep genere recursivamente (r) una lista (l) de todos los archivos que contienen <string-to-match> . xargs recorre estos archivos, llamando a grep -L sobre cada uno de ellos. grep -L solo generará el nombre del archivo cuando el archivo no contenga <string-not-to-match> .
Estas respuestas parecen estar fuera de lugar como la coincidencia AMBAS instrumentos de cuerda. El siguiente comando debería funcionar mejor:
grep -l <string-to-match> * | xargs grep -c <string-not-to-match> | grep '\:0'