231 votes

Comment puis-je faire en sorte que `find` ignore les répertoires .svn ?

J'utilise souvent le find pour rechercher dans le code source, supprimer des fichiers, etc. De manière ennuyeuse, parce que Subversion stocke des duplicatas de chaque fichier dans sa base de données de fichiers. .svn/text-base/ mes simples recherches aboutissent à de nombreux résultats en double. Par exemple, je veux rechercher de façon récursive uint dans de multiples messages.h y messages.cpp des fichiers :

# find -name 'messages.*' -exec grep -Iw uint {} +
./messages.cpp:            Log::verbose << "Discarding out of date message: id " << uint(olderMessage.id)
./messages.cpp:    Log::verbose << "Added to send queue: " << *message << ": id " << uint(preparedMessage->id)
./messages.cpp:                Log::error << "Received message with invalid SHA-1 hash: id " << uint(incomingMessage.id)
./messages.cpp:            Log::verbose << "Received " << *message << ": id " << uint(incomingMessage.id)
./messages.cpp:            Log::verbose << "Sent message: id " << uint(preparedMessage->id)
./messages.cpp:        Log::verbose << "Discarding unsent message: id " << uint(preparedMessage->id)
./messages.cpp:        for (uint i = 0; i < 10 && !_stopThreads; ++i) {
./.svn/text-base/messages.cpp.svn-base:            Log::verbose << "Discarding out of date message: id " << uint(olderMessage.id)
./.svn/text-base/messages.cpp.svn-base:    Log::verbose << "Added to send queue: " << *message << ": id " << uint(preparedMessage->id)
./.svn/text-base/messages.cpp.svn-base:                Log::error << "Received message with invalid SHA-1 hash: id " << uint(incomingMessage.id)
./.svn/text-base/messages.cpp.svn-base:            Log::verbose << "Received " << *message << ": id " << uint(incomingMessage.id)
./.svn/text-base/messages.cpp.svn-base:            Log::verbose << "Sent message: id " << uint(preparedMessage->id)
./.svn/text-base/messages.cpp.svn-base:        Log::verbose << "Discarding unsent message: id " << uint(preparedMessage->id)
./.svn/text-base/messages.cpp.svn-base:        for (uint i = 0; i < 10 && !_stopThreads; ++i) {
./virus/messages.cpp:void VsMessageProcessor::_progress(const string &fileName, uint scanCount)
./virus/messages.cpp:ProgressMessage::ProgressMessage(const string &fileName, uint scanCount)
./virus/messages.h:    void _progress(const std::string &fileName, uint scanCount);
./virus/messages.h:    ProgressMessage(const std::string &fileName, uint scanCount);
./virus/messages.h:    uint        _scanCount;
./virus/.svn/text-base/messages.cpp.svn-base:void VsMessageProcessor::_progress(const string &fileName, uint scanCount)
./virus/.svn/text-base/messages.cpp.svn-base:ProgressMessage::ProgressMessage(const string &fileName, uint scanCount)
./virus/.svn/text-base/messages.h.svn-base:    void _progress(const std::string &fileName, uint scanCount);
./virus/.svn/text-base/messages.h.svn-base:    ProgressMessage(const std::string &fileName, uint scanCount);
./virus/.svn/text-base/messages.h.svn-base:    uint        _scanCount;

Comment puis-je dire find d'ignorer le .svn des répertoires ?


Mise à jour : Si vous mettez à jour votre client SVN vers version 1.7 ce n'est plus un problème.

Une caractéristique clé des changements introduits dans Subversion 1.7 est la centralisation du stockage des métadonnées de la copie de travail en un seul endroit. Au lieu d'un .svn dans chaque répertoire de la copie de travail, les copies de travail de Subversion 1.7 n'ont qu'un seul répertoire. .svn dans le répertoire Root de la copie de travail. Ce répertoire inclut (entre autres choses) une base de données soutenue par SQLite qui contient toutes les métadonnées dont Subversion a besoin pour cette copie de travail.

4 votes

Pour des raisons de performance, essayez d'utiliser find ... -print0 | xargs -0 egrep ... au lieu de find ... -exec grep ... (ne bifurque pas grep pour chaque fichier, mais pour un groupe de fichiers à la fois). En utilisant ce formulaire, vous pouvez également élaguer .svn sans utiliser l'option -prune option de recherche, c'est-à-dire find ... -print0 | egrep -v '/\.svn' | xargs -0 egrep ...

3 votes

@Vlad : Pour autant que je sache, l'utilisation de -exec con + ne fourche pas grep pour chaque fichier, tout en l'utilisant avec ; fait. Utilisation de -exec est en fait plus correct que d'utiliser xargs . Veuillez noter que des commandes comme ls font quelque chose même si la liste d'arguments est vide, tandis que des commandes comme chmod donner une erreur si les arguments sont insuffisants. Pour voir ce que je veux dire, essayez simplement la commande suivante dans un répertoire qui ne contient pas de shell script : find /path/to/dir -name '*.sh' -print0 | xargs -0 chmod 755 . Comparez avec celui-ci : find /path/to/dir -name '*.sh' -exec chmod 755 '{}' '+' .

2 votes

@Vlad : D'ailleurs, grep -sortir .svn n'est pas non plus une bonne idée. Alors que find est spécialisé dans la gestion des propriétés des fichiers, grep ne le fait pas. Dans votre exemple, un fichier nommé '.svn.txt' sera également filtré par votre egrep commandement. Bien que vous puissiez modifier votre regex pour '^/ \.svn $' mais ce n'est toujours pas une bonne pratique de le faire. Le site -prune prédicat de find fonctionne parfaitement pour filtrer un fichier (par nom de fichier, ou par date de création, ou toute autre condition que vous avez fournie). C'est comme si vous pouviez tuer un cafard à l'aide d'une grande épée, cela ne veut pas dire que c'est la meilleure façon de le faire :-).

299voto

whaley Points 8789

Pourquoi ne pas simplement

find . -not -iwholename '*.svn*'

Le prédicat -not annule tout ce qui a un .svn n'importe où dans le chemin.

Donc, dans votre cas, ce serait

find -not -iwholename '*.svn*' -name 'messages.*' -exec grep -Iw uint {} + \;

5 votes

Super gros +1 pour "-not" et "-iwholename". Ack est merveilleux et je l'utilise, mais find/exec a toujours son utilité.

9 votes

La seule réponse qui répondait réellement à la question initiale.

0 votes

C'est génial. Je suis intéressé à trouver seulement les répertoires dans un WC SVN afin que je puisse faire un nettoyage récursif. Ajout de -type d à la ligne supérieure ci-dessus me donne exactement ce dont j'ai besoin.

144voto

Kaleb Pederson Points 22428

Comme suit :

find . -path '*/.svn*' -prune -o -print

Ou, alternativement, basé sur un répertoire et non un préfixe de chemin :

find . -name .svn -a -type d -prune -o -print

14 votes

@Kaleb : Salut. Je suggère find . -type d -name .svn -prune -o -print parce que c'est un peu plus rapide. Selon le Norme POSIX les expressions sont évaluées une par une, dans l'ordre spécifié. Si la première expression dans -a es false la deuxième expression ne sera pas évaluée (également appelé court-circuit et évaluation ).

2 votes

@Kaleb : En comparant les type de fichier (équivalent à tester si un bit est activé dans un entier) est plus rapide que de comparer les nom de fichier (équivalent à une comparaison de chaînes de caractères, qui est O(n)), en mettant -type d avant -name .svn est théoriquement plus efficace. Cependant, elle est généralement insignifiante, sauf si vous avez une très très grande arborescence de répertoires.

0 votes

@Siu - Bon point. De même, si vous avez un contrôle qui peut être effectué rapidement (par exemple O(1)) et qui évitera de nombreux contrôles supplémentaires, c'est une bonne idée de placer ce contrôle en premier.

67voto

Brian Agnew Points 143181

Pour la recherche, je peux vous suggérer de regarder ack ? Il s'agit d'un code source conscient find et, en tant que tel, ignorera automatiquement de nombreux types de fichiers, y compris les informations de dépôt de code source telles que celles mentionnées ci-dessus.

3 votes

J'aime ack mais j'ai constaté qu'il était nettement plus lent que les find -type f -name "*.[ch]" | xargs grep lorsqu'il s'agit d'un code de base important.

64 votes

John, je suis l'auteur de ack, et si vous pouvez me donner des détails sur les problèmes de vitesse de ack par rapport à grep, je vous en serais reconnaissant. Ils sont tout à fait comparables dans tous les cas que j'ai trouvés. Vous pouvez me le faire savoir à github.com/petdance/ack/issues ou envoyez-moi un courriel à andy at petdance.com. Merci.

64 votes

Les gars, c'est un conseil, mais certainement pas une réponse à la question ! :)

20voto

Antoine Points 2887

Voici ce que je ferais dans votre cas :

find . -path .svn -prune -o -name messages.* -exec grep -Iw uint {} +

Emacs rgrep La commande intégrée ignore .svn et beaucoup d'autres fichiers qui ne vous intéressent probablement pas lorsque vous effectuez un find | grep . Voici ce qu'il utilise par défaut :

find . \( -path \*/SCCS -o -path \*/RCS -o -path \*/CVS -o -path \*/MCVS \
          -o -path \*/.svn -o -path \*/.git -o -path \*/.hg -o -path \*/.bzr \
          -o -path \*/_MTN -o -path \*/_darcs -o -path \*/\{arch\} \) \
     -prune -o \
       \( -name .\#\* -o -name \*.o -o -name \*\~ -o -name \*.bin -o -name \*.lbin \
          -o -name \*.so -o -name \*.a -o -name \*.ln -o -name \*.blg \
          -o -name \*.bbl -o -name \*.elc -o -name \*.lof -o -name \*.glo \
          -o -name \*.idx -o -name \*.lot -o -name \*.fmt -o -name \*.tfm \
          -o -name \*.class -o -name \*.fas -o -name \*.lib -o -name \*.mem \
          -o -name \*.x86f -o -name \*.sparcf -o -name \*.fasl -o -name \*.ufsl \
          -o -name \*.fsl -o -name \*.dxl -o -name \*.pfsl -o -name \*.dfsl \
          -o -name \*.p64fsl -o -name \*.d64fsl -o -name \*.dx64fsl -o -name \*.lo \
          -o -name \*.la -o -name \*.gmo -o -name \*.mo -o -name \*.toc \
          -o -name \*.aux -o -name \*.cp -o -name \*.fn -o -name \*.ky \
          -o -name \*.pg -o -name \*.tp -o -name \*.vr -o -name \*.cps \
          -o -name \*.fns -o -name \*.kys -o -name \*.pgs -o -name \*.tps \
          -o -name \*.vrs -o -name \*.pyc -o -name \*.pyo \) \
     -prune -o \
     -type f \( -name pattern \) -print0 \
     | xargs -0 -e grep -i -nH -e regex

Il ignore les répertoires créés par la plupart des systèmes de contrôle de version, ainsi que les fichiers générés pour de nombreux langages de programmation. Vous pouvez créer un alias qui invoque cette commande et remplacer find y grep pour vos problèmes spécifiques.

13voto

ghostdog74 Points 86060

GNU find

find .  ! -regex ".*[/]\.svn[/]?.*"

0 votes

Je chargeais les chemins des répertoires dans un tableau pour que PHP les traite. Les autres réponses plus haut (pour une raison ou une autre) ne filtraient pas les fichiers dans la recherche (malgré l'option -type d ) - cette réponse l'a fait. +1

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X