2074 votes

Comment exclure un répertoire dans la commande find .

J'essaie d'exécuter un find pour tous les fichiers JavaScript, mais comment exclure un répertoire spécifique ?

Voici le find que nous utilisons.

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done

11 votes

Quel est le répertoire que vous devez exclure ?

13 votes

Il est préférable d'utiliser find ... | while read -r file ... . De plus, il est préférable d'accepter et d'upvoter les réponses.

0 votes

Alors que la lecture est lente, le for in est plus rapide

2588voto

GetFree Points 4971

Si -prune ne fonctionne pas pour vous, ceci le fera :

find -name "*.js" -not -path "./directory/*"

Attention : nécessite de traverser tous les répertoires non désirés.

3 votes

Décrivez pourquoi ils ont tort ? J'utilise la réponse choisie depuis deux ans et demi maintenant...

114 votes

L'un des commentaires de la réponse acceptée signale le problème. -prune n'exclut pas le répertoire lui-même, il exclut son contenu, ce qui signifie que vous allez obtenir une ligne indésirable dans la sortie avec le répertoire exclu.

167 votes

Excellente réponse. J'ajouterais à cela que vous pouvez exclure un répertoire à N'IMPORTE QUEL niveau en modifiant le premier paramètre . a * . donc find -name "*.js" -not -path "*/omitme/*" omettrait les fichiers d'un répertoire nommé "omitme" à n'importe quel niveau de profondeur.

1709voto

f10bit Points 2087

Utilisez le -prune commutateur. Par exemple, si vous souhaitez exclure le commutateur misc ajoutez simplement un -path ./misc -prune -o à votre commande de recherche :

find . -path ./misc -prune -false -o -name '*.txt'

Voici un exemple avec plusieurs répertoires :

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -false -o -name '*.txt'

Ici, nous excluons ./dir1 , ./dir2 y ./dir3 dans le répertoire courant, puisque dans find expressions c'est une action qui agit sur les critères -path dir1 -o -path dir2 -o -path dir3 (si dir1 o dir2 o dir3 ), ET avec type -d .

Pour exclure le nom du répertoire à n'importe quel niveau, utilisez -name :

find . -type d \( -name node_modules -o -name dir2 -o -path name \) -prune -false -o -name '*.json'

107 votes

Hmm. Cela ne fonctionne pas pour moi non plus car cela inclut le répertoire ignoré "./misc" dans la sortie.

96 votes

@Theuni Cela n'a probablement pas fonctionné pour vous parce que vous n'avez pas ajouté un -print (ou toute autre action) explicitement après -name . Dans ce cas, les deux "côtés" de -o finissent par s'imprimer, alors que si vous utilisez -print seul ce côté est imprimé.

0 votes

Comme je l'ai expliqué dans un commentaire ci-dessous, je viens d'exécuter mon exemple et celui de GetFree et ils renvoient le même résultat : pastebin.com/eH4tvgSh J'utilise findutils 4.4.2 et bash 4.2.045.

607voto

Daniel C. Sobral Points 159554

Je trouve la solution suivante plus facile à raisonner que les autres solutions proposées :

find build -not \( -path build/external -prune \) -name \*.js
# you can also exclude multiple paths
find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

Note importante : les chemins que vous tapez après -path doit correspondre exactement à ce que find s'imprimerait sans l'exclusion. Si cette phrase vous perturbe, assurez-vous simplement d'utiliser les chemins complets tout au long de l'opération. ensemble du site comme ceci : find **/full/path/** -not \( -path **/full/path/exclude/this** -prune \) ... . Voir la note [1] si vous souhaitez mieux comprendre.

À l'intérieur de \( y \) est une expression qui correspondra à exactement build/external (voir la note importante ci-dessus), et le fera, en cas de succès, éviter de traverser tout ce qui se trouve en dessous . Cette expression est ensuite regroupée en une seule expression avec les parenthèses échappées, et préfixée avec -not ce qui rendra find sauter tout ce qui correspond à cette expression.

On peut se demander si ajouter -not ne rendra pas tous les autres fichiers cachés par -prune réapparaître, et la réponse est non. La façon -prune est tout ce que, une fois qu'il est atteint, les fichiers situés sous ce répertoire sont définitivement ignorés.

Ceci provient d'un cas d'utilisation réel, où j'avais besoin d'appeler yui-compressor sur certains fichiers générés par wintersmith, mais laisser de côté d'autres fichiers qui doivent être envoyés tels quels.


Note [1] : Si vous voulez exclure /tmp/foo/bar et vous trouvez comme ceci " find /tmp \(... "alors vous devez spécifier -path /tmp/foo/bar . Si d'un autre côté vous trouvez comme ceci cd /tmp; find . \(... alors vous devez spécifier -path ./foo/bar .

55 votes

Excellente réponse, merci. Cela fonctionne et est évolutif (lisible) pour de multiples exclusions. Vous êtes un gentleman et un érudit monsieur. Merci pour l'exemple pour les exclusions multiples

8 votes

Cela ne fonctionne pas si je veux utiliser le commutateur -delete : find . -not \( -path ./CVS -prune \) -type f -mtime +100 -delete find: The -delete action atomatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.

21 votes

@Janis Vous pouvez utiliser -exec rm -rf {} \; au lieu de -delete .

278voto

BroSlow Points 3691

Il y a manifestement une certaine confusion quant à la syntaxe à utiliser pour sauter un répertoire.

Opinion de GNU

To ignore a directory and the files under it, use -prune

Extrait de la page de manuel GNU find

Raisonnement

-prune arrête find de descendre dans un répertoire. Il suffit de spécifier -not -path descendront encore dans le a sauté mais -not -path sera faux lorsque find teste chaque fichier.

Questions relatives à -prune

-prune fait ce qu'il est censé faire, mais il y a encore des choses auxquelles vous devez faire attention lorsque vous l'utilisez.

  1. find imprime le répertoire élagué.

    • VRAI C'est le comportement souhaité, mais il ne s'y abaisse pas. Pour éviter d'imprimer complètement le répertoire, utilisez une syntaxe qui l'omet logiquement.
  2. -prune ne fonctionne qu'avec -print et aucune autre action.

    • PAS VRAI . -prune fonctionne avec toutes les actions sauf -delete . Pourquoi ça ne marche pas avec la suppression ? Pour -delete pour fonctionner, find doit parcourir le répertoire dans l'ordre DFS, puisque -delete va d'abord supprimer les feuilles, puis les parents des feuilles, etc... Mais pour spécifier -prune pour avoir du sens, find doit atteindre un répertoire et arrêter de le descendre, ce qui n'a clairement aucun sens avec -depth o -delete sur.

Performance

J'ai mis en place un test simple des trois réponses les plus votées à cette question (remplacées par les réponses suivantes -print con -exec bash -c 'echo $0' {} \; pour montrer un autre exemple d'action). Les résultats sont les suivants

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

Conclusion

Les deux sites La syntaxe de f10bit y La syntaxe de Daniel C. Sobral a pris 10-25ms pour s'exécuter en moyenne. La syntaxe de GetFree qui n'utilise pas -prune a pris 865 ms. Donc, oui, c'est un exemple plutôt extrême, mais si vous vous souciez du temps d'exécution et que vous faites quelque chose d'un peu intensif, vous devriez utiliser -prune .

Note La syntaxe de Daniel C. Sobral a réalisé la meilleure performance des deux -prune mais je soupçonne fortement qu'il s'agit du résultat d'une mise en cache, car en changeant l'ordre dans lequel les deux s'exécutent, on obtient le résultat inverse, alors que la version sans prune est toujours la plus lente.

Test script

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup

26 votes

Merci pour cette très bonne analyse. En ce qui concerne "Je soupçonne fortement que c'est le résultat d'une mise en cache", vous pouvez exécuter la commande suivante : sudo sh -c "free && sync && echo 3 > /proc/sys/vm/drop_caches && free" pour vider le cache (cf. unix.stackexchange.com/questions/87908/ ).

0 votes

Après quelques tests sur ces deux-là avec -prune Je peux dire qu'il y a rarement une différence. Gardez à l'esprit que la commande qui démarre en premier bénéficiera des performances du processeur. réchauffement du processeur > baisse des performances cause un léger ralentissement (j'ai purgé le cache avant chaque commande comme le suggère @ndemou)

0 votes

Essayez le numéro de l'interrupteur parmi name1() name2() name3() dans le test @BroSlow script ci-dessus pour changer l'ordre d'exécution afin d'avoir un visuel sur ce que j'ai dit. Dans la vie réelle, c'est imperceptible entre ces deux-là.

48voto

mpapis Points 32015

Je préfère le -not la notation ... c'est plus lisible :

find . -name '*.js' -and -not -path directory

6 votes

Désolé, ça ne marche pas. La page de manuel de find dit : "Pour ignorer un répertoire et les fichiers qu'il contient, utilisez -prune".

11 votes

C'est faux. Cela n'empêche pas find d'entrer dans le répertoire et de parcourir tous les fichiers qu'il contient.

0 votes

find . -iname '*' -and -not -path './somePath' ne l'empêche pas d'entrer dans ledit répertoire.

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