253 votes

Comment utiliser l'option '-prune' de 'find' dans sh ?

Je n'ai pas bien compris l'exemple donné dans le 'man find', quelqu'un peut-il me donner des exemples et des explications ? Puis-je y combiner des expressions régulières ?


la question plus détaillée est la suivante : écrivez un shell script, changeall, qui a une interface comme "changeall [-r|-R] "string1" "string2". Il trouvera tous les fichiers dont le suffixe est .h, .C, .cc ou .cpp et changera toutes les occurrences de "string1" en "string2". -r est une option pour rester dans le répertoire courant seulement ou inclure les sous-répertoires. NOTE : 1) pour le cas non-récursif, 'ls' n'est PAS autorisé, nous ne pouvons utiliser que 'find' et 'sed'. 2) J'ai essayé 'find -depth' mais il n'est PAS supporté. C'est pourquoi je me suis demandé si '-prune' pouvait aider, mais je n'ai pas compris l'exemple de 'man find'.


EDIT2 : J'étais en train de faire un devoir, je n'ai pas posé la question en détail parce que j'aimerais le terminer moi-même. Comme je l'ai déjà fait et que je l'ai rendu, je peux maintenant énoncer toute la question. De plus, j'ai réussi à terminer le devoir sans utiliser -prune, mais j'aimerais quand même l'apprendre.

507voto

Laurence Gonsalves Points 50783

La chose que j'ai trouvée déroutante à propos de -prune est qu'il s'agit d'une action (comme -print ), et non un test (comme -name ). Il modifie la liste des "choses à faire", mais retourne toujours vrai .

Le modèle général d'utilisation des -prune c'est ça :

find [path] [conditions to prune] -prune -o [your usual conditions] [actions to perform]

Vous voulez presque toujours le -o immédiatement après -prune parce que la première partie de l'épreuve (jusqu'à l'inclusion des éléments suivants -prune ) renverra faux pour les éléments que vous souhaitez réellement (c'est-à-dire les éléments que vous ne voulez pas supprimer).

Voici un exemple concret :

find . -name .snapshot -prune -o -name '*.foo' -print

Cela trouvera les fichiers "*.foo" qui ne sont pas dans les répertoires ".snapshot". Dans cet exemple, -name .snapshot est le "test pour les choses que vous voulez élaguer", et -name '*.foo' -print est le "truc que vous mettriez normalement après le chemin".

Remarques importantes :

  1. Si tout ce que vous voulez faire est d'imprimer les résultats, vous avez peut-être l'habitude de laisser de côté l'option -print action. En général, vous no voulez faire cela lorsque vous utilisez -prune .

    Le comportement par défaut de find est de "et" l'objet de la recherche. tout le site avec l'expression -print s'il n'y a pas d'autres actions que -prune (ironiquement) à la fin. Cela signifie qu'en écrivant ceci :

    find . -name .snapshot -prune -o -name '*.foo'                 # DON'T DO THIS

    est équivalent à écrire ceci :

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print    # DON'T DO THIS

    ce qui signifie qu'il imprimera également le nom du répertoire que vous élaguez, ce qui n'est généralement pas ce que vous voulez.

  2. Si votre "condition habituelle" correspond à des fichiers qui correspondent également à votre condition d'élagage, ces fichiers seront no être inclus dans la sortie. La façon de résoudre ce problème est d'ajouter un -type d prédicat à votre condition d'élagage.

    Par exemple, si nous voulons éliminer tout répertoire commençant par .git (il est vrai que c'est un peu artificiel -- normalement, il suffit de supprimer les choses nommées exactement .git ), mais je voulais toujours voir tous les fichiers, y compris les fichiers comme .gitignore vous pouvez essayer ceci :

    find . -name '.git*' -prune -o -type f -print  # DON'T DO THIS

    Ce serait no inclure .gitignore dans la sortie. Voici la version corrigée :

    find . -type d -name '.git*' -prune -o -type f -print

Conseil supplémentaire : si vous utilisez la version GNU de find , la page de texinfo pour find a une explication plus détaillée que sa page de manuel (comme c'est le cas pour la plupart des utilitaires GNU).

35voto

AmitP Points 864

Normalement, la manière native dont nous faisons les choses dans linux et la manière dont nous pensons est de gauche à droite.
Vous devez donc commencer par écrire ce que vous cherchez :

find / -name "*.php"

Ensuite, vous appuyez probablement sur entrée et vous réalisez que vous obtenez trop de fichiers à partir de répertoires que vous ne souhaitez pas. Excluons /media pour éviter de chercher dans vos disques montés.
Vous devez maintenant APPENDRE ce qui suit à la commande précédente :

-print -o -path '/media' -prune

donc la commande finale est :

find / -name "*.php" -print -o -path '/media' -prune

...............|<--- Inclure --->|....................|<---------- Exclure --------->||.

Je pense que cette structure est beaucoup plus facile et correspond à la bonne approche.

28voto

Dennis Williamson Points 105818

Attention, l'option -prune n'empêche pas de descendre dans le domaine de la santé. tout répertoire comme certains l'ont dit. Il empêche de descendre dans les répertoires qui correspondent au test auquel il est appliqué. Peut-être que quelques exemples vous aideront (voir en bas de page pour un exemple de regex). Désolé d'avoir été si long.

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

15voto

crw Points 363

En complément des conseils donnés dans d'autres réponses (je n'ai pas de rep pour créer des réponses)...

En combinant -prune avec d'autres expressions, il existe une différence subtile de comportement selon les autres expressions utilisées.

L'exemple de @Laurence Gonsalves trouvera les fichiers "*.foo" qui ne se trouvent pas dans les répertoires ".snapshot" :-)

find . -name .snapshot -prune -o -name '*.foo' -print

Toutefois, cette abréviation légèrement différente énumérera également, peut-être par inadvertance, les éléments suivants .snapshot (et tous les répertoires .snapshot imbriqués):-

find . -name .snapshot -prune -o -name '*.foo'

La raison en est (selon la page de manuel de mon système):-

Si l'expression donnée ne contient aucune des primitives -exec, -ls, -ok, ou -print, l'expression donnée est effectivement remplacée par :

( expression_donnée ) -print

En d'autres termes, le deuxième exemple équivaut à la saisie de ce qui suit, modifiant ainsi le regroupement des termes :-.

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

Cela a été observé au moins sur Solaris 5.10. Ayant utilisé diverses saveurs de *nix pendant environ 10 ans, je n'ai que récemment cherché la raison pour laquelle cela se produit.

2voto

AdamW Points 935

Prune est un "do not recurse" à tout changement de répertoire.

Extrait de la page de manuel

Si -depth n'est pas donné, true ; si le fichier est un répertoire, ne pas y descendre. Si -depth est donné, false ; aucun effet.

En fait, il ne se décompose pas en sous-répertoires.

Prenez cet exemple :

Vous avez les répertoires suivants

  • /home/test2
  • /home/test2/test2

Si vous exécutez find -name test2 :

Il retournera les deux répertoires

Si vous exécutez find -name test2 -prune :

Il retournera seulement /home/test2 car il ne descendra pas dans /home/test2 pour trouver /home/test2/test2

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