Comment trouver des fichiers qui ne contiennent pas un modèle de chaîne donné?

536

Comment trouver les fichiers du répertoire courant qui ne contiennent pas le mot foo(en utilisant grep)?

Senthil Kumar
la source

Réponses:

818

Si votre grep a l' option -L(ou --files-without-match):

$ grep -L "foo" *
ghostdog74
la source
1
Comme indiqué ailleurs, ack permet d'éviter les fichiers .svn (subversion) par défaut.
GuruM
11
@GuruM Cela peut être fait dans GNU grep en exportant la variable GREP_OPTIONS='--exclude-dir=.svn --exclude-dir=.git': ^)
bufh
6
Ou l'équivalent en utilisant ag :ag -L 'foo'
évêque
5
Fonctionne comme par magie! Astuce: utiliser -rLau lieu de -Lpour faire correspondre les sous
Ufos
1
@Larry - Une manière plus propre d'éviter les problèmes de globalisation est d'utiliser l'option longue "vide" comme ceci: grep -L 'foo' -- *La norme est que les commandes qui prennent des options longues utilisent --pour indiquer qu'il n'y a plus d'options après ce point.
Paddy Landau
45

Jetez un oeil à ack. Il effectue .svnautomatiquement l' exclusion pour vous, vous donne des expressions régulières Perl et est un simple téléchargement d'un seul programme Perl.

L'équivalent de ce que vous recherchez devrait être ack:

ack -L foo
Andy Lester
la source
24

Vous pouvez le faire avec grep seul (sans trouver).

grep -riL "foo" .

Ceci est l'explication des paramètres utilisés sur grep

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

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

Si vous utilisez l(en minuscules) vous obtiendrez le contraire (fichiers avec correspondances)

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

La commande suivante me donne tous les fichiers qui ne contiennent pas le motif foo:

find .  -not  -ipath '.*svn*' -exec  grep  -H -E -o -c  "foo"  {} \; | grep 0
Senthil Kumar
la source
4
Vous voulez changer le grep 0 à la fin en grep 0 $ (sinon vous obtenez des correspondances erronées sur les fichiers qui ont le caractère 0 dans leur nom de fichier).
clouseau
9
@clouseau a généralement raison ... Cependant, grep '0$'correspondrait aussi à des fichiers avec des multiples de 10 lignes! Vous devez grep ':0$'à la fin vérifier un ': 0' explicite à la fin de la ligne. Ensuite, vous n'obtiendrez que des fichiers avec zéro ligne correspondante.
TrinitronX
L'UNIX sur lequel je suis n'avait pas de versions de find ou grep avec ces options, j'ai donc dû utiliser la commande "ack" suggérée dans d'autres commentaires.
KC Baltz
14

La commande suivante exclut la nécessité pour la recherche de filtrer les svndossiers à l'aide d'une seconde grep.

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

Vous aurez en fait besoin de:

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

J'ai eu de la chance avec

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

Mes tentatives avec grep -vjuste m'ont donné toutes les lignes sans "foo".

Johnny
la source
4

Problème

J'ai besoin de refactoriser un grand projet qui utilise des .phtmlfichiers pour écrire du HTML en utilisant du code PHP en ligne. Je souhaite utiliser à la place des modèles Moustache . Je veux trouver tous les .phtmlgiles qui ne contiennent pas la chaîne new Mustachecar ils doivent encore être réécrits.

Solution

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

Explication

Avant les tuyaux:

Trouver

find . Rechercher des fichiers récursivement, en commençant dans ce répertoire

-iname '*.phtml'Le nom de fichier doit contenir .phtml(le irend insensible à la casse)

-exec 'grep -H -E -o -c 'new Mustache' {}'Exécutez la grepcommande sur chacun des chemins correspondants

Grep

-H Imprimez toujours les en-têtes de nom de fichier avec les lignes de sortie.

-E Interpréter le motif comme une expression régulière étendue (c'est-à-dire forcer grep à se comporter comme egrep).

-o Imprime uniquement la partie correspondante des lignes.

-c Seul un nombre de lignes sélectionnées est écrit sur la sortie standard.


Cela me donnera une liste de tous les chemins de fichiers se terminant par .phtml, avec un nombre de fois où la chaîne new Mustachese produit dans chacun d'eux.

$> 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

Le premier canal grep :0$filtre cette liste pour inclure uniquement les lignes se terminant par :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

Le deuxième canal sed 's/..$//'supprime les deux derniers caractères de chaque ligne, ne laissant que les chemins de fichier.

$> 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
Gruffy
la source
3

Si vous utilisez git, cela recherche tous les fichiers suivis:

git grep -L "foo"

et vous pouvez effectuer une recherche dans un sous-ensemble de fichiers suivis si vous avez activé ** globbing de sous-répertoire ( shopt -s globstardans .bashrc, voir ceci ):

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

Mon grep n'a pas d'option -L. Je trouve une solution pour y parvenir.

Les idées sont:

  1. pour vider tout le nom de fichier contenant la chaîne méritée dans un txt1.txt.
  2. vider tout le nom de fichier du répertoire dans un txt2.txt.
  3. faire la différence entre le 2 fichier de vidage avec la commande diff.

    grep 'foo' *.log | cut -c1-14 | uniq > txt1.txt
    grep * *.log | cut -c1-14 | uniq > txt2.txt
    diff txt1.txt txt2.txt | grep ">"
    
user6305682
la source
J'oublie les commandes mais au lieu de vider les noms de fichiers, vous pouvez réellement faire diffentre deux flux de sortie (je pense que vous entourez les commandes de parenthèses, et il y a aussi un angle entre les deux), si vos systèmes le prennent en charge, ce que je suppose est la question, car il ne prend pas en chargegrep -L
Dexygen
1

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

Vous pouvez spécifier le filtre sous "trouver" et la chaîne d'exclusion sous "grep -vwE". Utilisez mtime under find si vous devez également filtrer l'heure modifiée.

zandeep
la source
Cela semble me montrer toutes les lignes sans la chaîne, l'OP ne demande que les noms de fichiers.
Ben Farmer
1

Rapport de bogue ouvert

Comme l'a commenté @tukan, il existe un rapport de bogue ouvert pour Ag concernant le drapeau -L/ --files-without-matches:

Comme il y a peu de progrès dans le rapport de bogue, l' -Loption mentionnée ci-dessous ne doit pas être invoquée , pas tant que le bogue n'a pas été résolu. Utilisez plutôt les différentes approches présentées dans ce fil. Citant un commentaire pour le rapport de bogue [c'est moi qui souligne]:

Des mises à jour à ce sujet? -Lignore complètement les correspondances sur la première ligne du fichier. Il semble que si cela ne va pas être corrigé bientôt, le drapeau devrait être supprimé complètement, car il ne fonctionne pas du tout comme annoncé .


The Silver Searcher - Ag (fonction prévue - voir rapport de bogue)

Comme alternative puissante à grep, vous pouvez utiliser The Silver Searcher - Ag :

Un outil de recherche de code similaire à ack, avec un accent sur la vitesse.

En regardant man ag, nous trouvons l' option -Lou --files-without-matches:

...

OPTIONS
    ...

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

C'est-à-dire, pour rechercher récursivement des fichiers qui ne correspondent pas foo, à partir du répertoire courant:

ag -L foo

Pour rechercher uniquement dans le répertoire actuel les fichiers qui ne correspondent pas foo, spécifiez simplement --depth=0la récursivité:

ag -L foo --depth 0
dfri
la source
Cela échoue de temps en temps en raison du -Lbogue - github.com/ggreer/the_silver_searcher/issues/238
tukan
@tukan merci pour l'invite. J'ai mis à jour la réponse; choisir de ne pas supprimer la réponse mais plutôt d'ouvrir avec les informations concernant le bug.
dfri
1

une autre alternative lorsque grep n'a pas l'option -L (IBM AIX par exemple), avec rien d'autre que grep et le shell:

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

ou

grep -ve "pattern" < file

la commande ci-dessus nous donnera le résultat car -v trouve l'inverse du motif recherché

Geai
la source
1
Ceci imprime les lignes qui ne contiennent pas le motif. Vous pouvez ajouter l' -loption d'imprimer uniquement le nom du fichier; mais cela affiche toujours les noms de tout fichier contenant une ligne qui ne contient pas le motif. Je crois que l'OP veut trouver les fichiers qui ne contiennent aucune ligne contenant le motif.
tripleee
La commande que vous avez fournie répertorie les fichiers dans "filepath" avec toutes leurs lignes qui ne contiennent pas "pattern".
2018 à 19h32
-6

La commande suivante peut vous aider à filtrer les lignes qui incluent la sous-chaîne "foo".

cat file | grep -v "foo"
walkerlin
la source
2
Ceci imprime des lignes qui ne correspondent pas, pas des noms de fichiers qui ne contiennent aucune correspondance sur aucune ligne. Pour ajouter l'insulte à la blessure, il s'agit d'une utilisation inutile decat .
tripleee