Comment ignorer certains noms de fichiers en utilisant "find"?

143

Une de mes commandes BASH préférées est:

find . -name '*.*' -exec grep 'SearchString' {} /dev/null \;

qui recherche le contenu spécifié de SearchString dans le contenu de tous les fichiers situés dans et sous le répertoire en cours. En tant que développeur, cela s’est parfois révélé utile.

Cependant, en raison de mon projet actuel et de la structure de ma base de code, j'aimerais rendre cette commande BASH encore plus évoluée en ne recherchant aucun fichier situé dans ou sous un répertoire contenant ".svn", ni aucun fichier se termine par ".html"

La page MAN pour trouver un peu me confond cependant. J'ai essayé d'utiliser -une, et cela m'a donné un comportement étrange. En essayant de ne sauter que les pages .html (pour commencer), j'ai essayé:

find . -wholename './*.html' -prune -exec grep 'SearchString' {} /dev/null \;

et n'a pas eu le comportement que j'espérais. Je pense que je pourrais manquer le point de -prune. Pourriez-vous m'aider?

Merci

Cody S
la source
1
Just fyi: findn'est pas une commande
intégrée
1
Vous pouvez effectuer une recherche dans le fichier avecgrep -rl 'SearchString'
emanuele
@emanuele Bonjour, bienvenue sur SuperUser (et sur le réseau Stack Exchange). C'est une question que j'ai posée et à laquelle on a répondu il y a deux ans et demi. Généralement, si vous souhaitez ajouter une réponse à la question, faites-le en faisant défiler l'écran jusqu'au bas de la page et répondez-y, plutôt que dans un commentaire. Étant donné que cette question a déjà une réponse acceptée (celle avec la coche verte), il est peu probable que votre réponse suscite beaucoup d'attention, cependant. FYI.
Cody S
1
Bonjour, ce n'est pas une réponse à votre question. Comme vous l'avez dit dans le préambule, il ne s'agit que d'une astuce qui consiste findà rechercher dans un fichier.
emanuele
2
FWIW, -name '*.*'ne trouve pas tous les fichiers: uniquement ceux qui ont un .nom (l’utilisation de *.*est typiquement un DOS-isme, alors qu’en Unix, vous n’utilisez normalement que *pour cela). Pour vraiment faire correspondre tous, il suffit de retirer l'argument tout à fait: find . -exec .... Ou si vous souhaitez uniquement appliquer grep aux fichiers (et ignorer les répertoires), faites-le find . -type f -exec ....
Stefan

Réponses:

197

Vous pouvez utiliser la fonction negate (!) De find pour ne pas faire correspondre les fichiers portant des noms spécifiques:

find . ! -name '*.html' ! -path '*.svn*' -exec grep 'SearchString' {} /dev/null \;

Donc, si le nom se termine par .html ou contient .svn n'importe où dans le chemin, il ne correspondra pas et l'exécution ne sera donc pas exécutée.

Paul
la source
1
Dois-je quand même spécifier -name ' . quelque part là-bas? Est-ce que je le ferais avant ou après les négociations?
Cody S
L'intention de votre *.*correspondance était-elle de s'assurer que seuls les fichiers correspondants contenant un .? Find correspondra à tous les fichiers en l'absence de namedirective, de sorte que ce qui précède correspondra à tout sauf html et svn
Paul
5
Je pense que vous voulez -wholename '*.svn*'plutôt que -name.
Fuenfundachtzig
2
Oui, cela signifie que les .svnrépertoires sont exclus des résultats de la recherche.
Fuenfundachtzig
1
@Noumenon ! -name '.'doit exclure .des résultats de la recherche.
Paul
11

J'ai le même problème depuis longtemps et plusieurs solutions peuvent être appliquées dans différentes situations:

  • ack-grepest une sorte de "développeur grep" qui ignore par défaut les répertoires de contrôle de version et les fichiers temporaires. La manpage explique comment rechercher uniquement des types de fichiers spécifiques et comment définir les vôtres .
  • grepLes propres options --excludeet --exclude-diroptions peuvent être utilisées très facilement pour ignorer les fichiers globaux et les répertoires simples (pas de globbing pour les répertoires, malheureusement).
  • find . \( -type d -name '.svn' -o -type f -name '*.html' \) -prune -o -print0 | xargs -0 grep ... devrait fonctionner, mais les options ci-dessus sont probablement moins compliquées à long terme.
l0b0
la source
9

La findcommande suivante élimine les répertoires dont le nom contient .svn . Bien qu’elle ne descende pas dans le répertoire, le nom du chemin élagué est imprimé ... ( -name '*.svn'c’est la cause!) ..

Vous pouvez filtrer les noms de répertoire via: grep -d skipqui ignore en silence ces entrées "noms de répertoire".

Avec GNU grep, vous pouvez utiliser à la -Hplace de /dev/null. Comme un problème secondaire: \+peut être beaucoup plus rapide que \;, par exemple. pour 1 million de fichiers d'une ligne, son utilisation \;a pris 4m20s , son utilisation \+n'a pris que 1.2s .

La méthode suivante utilise à la xargsplace de -exec, et suppose qu'il n'y a pas de saut \nde ligne dans aucun de vos noms de fichier . Tel qu'utilisé ici, xargsc'est à peu près la même chose que find \+.

xargspeut transmettre des noms de fichiers contenant des espaces consécutifs en modifiant le délimiteur d’entrée '\n'avec l’ -doption.

Ceci exclut les répertoires dont les noms contiennent .svn et mettent en attente uniquement les fichiers ne se terminant pas par .html.

find . \( -name '*.svn*' -prune  -o ! -name '*.html' \) |
   xargs -d '\n' grep -Hd skip 'SearchString'
Peter.O
la source
1
Merci d'avoir signalé la \+variante de l'action -exec. Hourra pour les petits problèmes secondaires!
Christian Long
Bien sûr, comme +le shell n’est pas un caractère spécial, vous n’avez pas besoin de taper \avant.
Scott