find: prune n'ignore pas le chemin spécifié

13

Je dois exclure .gitde ma findrecherche. Pour y parvenir, j'utilise le -path ./.git -prunecommutateur:

$ find . -path ./.git -prune -o \( -type f -o -type l -o -type d \) | grep '.git'
./.git

Cependant, même si cela ignore le contenu du répertoire .git, il répertorie le répertoire lui-même. Ça marche quand j'ajoute-path ./.git -prune -o -print -a

find . -path ./.git -prune -o -print -a \( -type f -o -type l -o -type d \) | grep '.git'

Pourquoi est-ce nécessaire? Je pensais que les deux commandes devraient avoir la même sortie. La deuxième syntaxe est assez moche.

Martin Vegter
la source
4
Vous en avez besoin -print, sinon l'implicite -prints'applique à l'ensemble de la condition
Stéphane Chazelas
1
Voir aussi unix.stackexchange.com/q/102191/22565
Stéphane Chazelas

Réponses:

3

La manpage pour finddonne:

-prune True; if the file is a directory, do not  descend  into  it.  If
      -depth  is  given,  false;  no  effect.  Because -delete implies
      -depth, you cannot usefully use -prune and -delete together.

Donc, dans le premier exemple, ce n'est pas le cas, ce qui -path ./.git -pruneest faux et donc l'action par défaut ( -print) ne serait pas appelée, donc la ligne est imprimée.

Timo
la source
22

J'étais confus quant à la raison pour laquelle les répertoires élagués étaient également imprimés par la findcommande et certains autres détails complexes sur la façon dont cela -prunefonctionnait, mais j'ai pu le comprendre avec quelques exemples.

Pour exécuter les exemples ci-dessous, créez les répertoires et fichiers suivants.

mkdir aa
mkdir bb
touch file1
touch aa/file1
touch bb/file3

Pour créer cette structure:

$ tree

.
├── aa
   └── file1
├── bb
   └── file3
└── file1

Utilisez maintenant find pour rechercher les répertoires nommés aa. Pas de problème ici.

$ find . -type d -name aa
./aa

Recherchez tous les répertoires autres que aa et nous obtenons le répertoire actuel .et ./bb, ce qui est également logique.

$ find . -type d ! -name aa
.
./bb

Jusqu'à présent, tout va bien, mais lorsque nous l'utilisons -prune, find renvoie le répertoire que nous élaguons, ce qui m'a d'abord dérouté car je m'attendais à ce qu'il renvoie tous les autres répertoires et non celui en élagage.

$ find . -type d -name aa -prune
./aa

La raison pour laquelle il retourne le répertoire en cours d'élagage est expliquée, non pas dans la -prunesection des pages de manuel comme indiqué dans la réponse de Timo , mais dans la EXPRESSIONSsection:

Si l'expression ne contient aucune action autre que -prune,  -printest effectuée sur tous les fichiers pour lesquels l'expression est vraie.

ce qui signifie que, puisque l'expression correspond au aanom du répertoire, alors l'expression sera évaluée à true et elle sera imprimée car find ajoute implicitement un -printà la fin de la commande entière. -printCependant, cela n'ajoutera pas un si vous ajoutez vous-même l'action -o -printà la fin:

find . -type d -name aa -prune -o -print
.
./file1
./bb
./bb/file3

Ici, la commande find N'ajoute -printplus d' implicite , donc le répertoire que nous élaguons ( aa) ne sera pas imprimé.

Donc, enfin, si nous ajoutons une clause qui recherche des fichiers avec un modèle de file*nom de fichier après le -o, alors vous devez mettre un -printà la fin de cette deuxième clause comme ceci:

find . \( -type d -name aa -prune \) -o \( -type f -name 'file*' -print \)
./file1
./bb/file3

La raison pour laquelle cela fonctionne est la même: si vous ne mettez pas de a -printdans la deuxième clause, alors comme il n'y a aucune action autre que l' -pruneaction, find ajoutera -printautomatiquement un à la FIN de la commande, provoquant l' -pruneimpression de la clause répertoire élagué:

find . \( \( -type d -name aa -prune \) -o \( -type f -name 'file*' \) \) -print
./aa
./file1
./bb/file3

En général, vous devez placer la -printcommande dans la deuxième clause. Si vous le placez au milieu comme l'a fait l'affiche originale, cela ne fonctionnera pas correctement car les fichiers en cours d'élagage seront imprimés immédiatement et la deuxième clause n'aura pas la possibilité de choisir les fichiers souhaités:

find . \( -type d -name aa -prune -o -print \) -o \( -type f -name 'file*' \)
.
./file1
./bb
./bb/file3

Donc, malheureusement, l'affiche originale a mal reçu la commande ci-dessus en plaçant -printle mauvais endroit. Cela pourrait avoir fonctionné pour son cas spécifique, mais cela ne fonctionne pas dans le cas général.

Il y a des milliers de personnes qui ont du mal à comprendre comment ça -prunemarche. La findpage de manuel doit être mise à jour afin d'éviter la confusion mondiale sans fin à propos de cette commande.

Jerry Marbas
la source
Ma page de manuel (Arch Linux find-4.6.0) dit: "Si l'expression entière ne contient aucune action autre que -prune ou -print, -print est effectuée sur tous les fichiers pour lesquels l'expression entière est vraie." Mais fonctionne comme vous le décrivez.
x-yuri
0

de la manpage,

Pour ignorer une arborescence de répertoires entière, utilisez -prune plutôt que de vérifier chaque fichier de l'arborescence. Par exemple, pour ignorer le répertoire `src / emacs 'et tous les fichiers et répertoires qu'il contient, et imprimer les noms des autres fichiers trouvés, faites quelque chose comme ceci:

find . -path ./src/emacs -prune -o -print

Donc, vous pouvez peut-être utiliser:

find . -path ./.git -prune -o -print

Cela ne répertorie pas le .gitrépertoire.

pour spécifier un nom de fichier, vous pouvez faire comme ceci:

find . -path ./.git -prune , -name filename

Veuillez noter l' ,opérateur virgule.

jianyongli
la source
-1

Voici une alternative rapide et sale:

find | fgrep -v /.git

Je ne me souviens tout simplement pas de findla syntaxe complexe de ...

peter.slizik
la source