Question Comment exclure un répertoire dans find. commander


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

Voici le code de recherche que nous utilisons.

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

903
2017-11-17 22:57


origine


Réponses:


Utilisez le commutateur d'élagage, par exemple si vous souhaitez exclure misc répertoire juste ajouter un -path ./misc -prune -o à votre commande de recherche:

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

Voici un exemple avec plusieurs répertoires:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print

Ici, nous excluons dir1, dir2 et dir3, puisque dans find expressions c'est une action, qui agit sur les critères -path dir1 -o -path dir2 -o -path dir3 (si dir1 ou dir2 ou dir3), ANDed avec type -d. D'autres actions sont -o print, imprimez juste.


701
2017-11-17 23:00



Si -prune ça ne marche pas pour toi, ça va:

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

1518
2018-04-01 01:26



Je trouve ce qui suit plus facile à raisonner que d'autres solutions proposées:

find build -not \( -path build/external -prune \) -name \*.js

Cela vient d'un cas d'utilisation réel, où j'ai dû appeler yui-compresseur sur certains fichiers générés par wintersmith, mais laisser de côté les autres fichiers qui doivent être envoyés tels quels.

À l'intérieur \( et \) est une expression qui correspondra exactement  build/external (ce sera ne pas correspondre si vous avez fait find ./buildpar exemple - vous devez le changer pour ./build/external dans ce cas), et, en cas de succès, évitez de parcourir tout ce qui est en dessous. Ceci est ensuite groupé comme une seule expression avec la parenthèse échappée, et préfixé avec -not ce qui fera find ignore tout ce qui correspond à cette expression.

On pourrait demander si l'ajout -not ne fera pas tous les autres fichiers cachés par -prune réapparaître, et la réponse est non. Le chemin -prune fonctionne est tout ce qui, une fois qu'il est atteint, les fichiers en dessous de ce répertoire sont définitivement ignorés.

Cela est également facile à développer pour ajouter des exclusions supplémentaires. Par exemple:

find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

341
2018-05-16 19:01



Il y a clairement une certaine confusion ici quant à ce que la syntaxe préférée pour sauter un répertoire devrait être.

Avis GNU

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

À partir de la page de manuel de recherche GNU

Raisonnement

-prune arrêts find de descendre dans un répertoire. Juste en spécifiant -not -path va encore descendre dans le ignoré répertoire, mais -not -path sera faux à chaque fois find teste chaque fichier.

Problèmes avec -prune

-prune fait ce à quoi il est destiné, mais il y a encore des choses dont vous devez vous occuper lorsque vous l'utilisez.

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

    • VRAI C'est un comportement intentionnel, ça ne descend pas dedans. Pour éviter d'imprimer le répertoire, utilisez une syntaxe qui l'omet de manière logique.
  2. -prune ne fonctionne qu'avec -print et pas d'autres actions.

    • PAS VRAI. -prune fonctionne avec n'importe quelle action sauf -delete. Pourquoi ça ne marche pas avec delete? Pour -delete pour travailler, trouver les besoins de parcourir le répertoire dans l'ordre DFS, puisque -deleteva d'abord supprimer les feuilles, puis les parents des feuilles, etc ... Mais pour spécifier -prune de donner un sens, find doit frapper un répertoire et arrêter de le descendre, ce qui n'a aucun sens avec -depth ou -delete sur.

Performance

J'ai mis en place un test simple des trois réponses les mieux cotées sur cette question (remplacé -printavec -exec bash -c 'echo $0' {} \; pour montrer un autre exemple d'action). Les résultats sont ci-dessous

----------------------------------------------
# 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

Tous les deux La syntaxe de f10bit et La syntaxe de Daniel C. Sobral a pris 10-25ms pour fonctionner en moyenne. La syntaxe de GetFree, qui n'utilise pas -prune, a pris 865ms. 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 de loin, vous devriez utiliser -prune.

Remarque La syntaxe de Daniel C. Sobral effectué le meilleur des deux -prune syntaxes; mais, je soupçonne fortement ceci est le résultat d'une certaine mise en cache en tant que commutation de l'ordre dans lequel les deux ont fonctionné dans le résultat opposé, alors que la version non-élagage était toujours la plus lente.

Script de test

#!/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

153
2017-07-04 00:21



Une option serait d'exclure tous les résultats contenant le nom du répertoire avec grep. Par exemple:

find . -name '*.js' | grep -v excludeddir

44
2017-11-17 23:01



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

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

37
2017-11-17 23:22



Utilisez l'option -prune. Donc, quelque chose comme:

find . -type d -name proc -prune -o -name '*.js'

Le '-type d -name proc -prune' recherche uniquement les répertoires nommés proc à exclure.
Le '-o' est un opérateur 'OU'.


17
2017-11-17 23:01



C'est le format que j'ai utilisé pour exclure certains chemins:

$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"

J'ai utilisé ceci pour trouver tous les fichiers qui ne sont pas dans les chemins ". *":

$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"

13
2018-01-18 17:06



C'est le seul qui a fonctionné pour moi.

Recherche de "NameOfFile" à l'exclusion de "Directory". Mettre l'accent sur les étoiles *.

find / -name NameOfFile ! -path '*/Directory/*'

10
2018-03-15 10:02