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 

Utilisez le commutateur d’élagage, par exemple si vous souhaitez exclure le répertoire misc , ajoutez simplement -path ./misc -prune -o à votre commande find:

 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 on exclut dir1, dir2 et dir3, puisque dans les expressions find 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 le type -d . Une autre action est -o print , il suffit d’imprimer.

Si -prune ne fonctionne pas pour vous, ceci:

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

Je trouve 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ù je devais appeler yui-compressor sur certains fichiers générés par wintersmith, mais laisser de côté les autres fichiers qui doivent être envoyés tels quels.

Inside \( et \) est une expression qui correspond exactement à build/external (elle ne correspondra pas si vous avez find ./build , par exemple – vous devez le remplacer par ./build/external dans ce cas), et en cas de succès, évitera de traverser tout ce qui est en dessous . Celui-ci est alors groupé sous la forme d’une expression unique avec la parenthèse échappée et préfixé par -not ce qui fera que find saute tout ce qui correspond à cette expression.

On pourrait se demander si l’ajout de -not ne fera pas réapparaître tous les autres fichiers cachés par -prune , et la réponse est non. La façon dont fonctionne -prune est tout ce qui, une fois atteint, les fichiers sous ce répertoire sont définitivement ignorés.

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

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

Il existe clairement une certaine confusion quant à la syntaxe préférée pour ignorer un répertoire.

Opinion GNU

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

De la page de manuel de GNU find

Raisonnement

-prune arrête la find descendant dans un répertoire. Il suffit de spécifier -not -path pour descendre dans le répertoire ignoré , mais -not -path sera faux lorsque find -not -path chaque fichier.

Problèmes avec -prune

-prune fait ce à quoi il est destiné, mais vous devez toujours prendre certaines mesures lorsque vous l’utilisez.

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

    • VRAI C’est le comportement prévu, il ne fait que descendre. Pour éviter d’imprimer le répertoire en entier, utilisez une syntaxe qui omet logiquement.
  2. -prune ne fonctionne qu’avec -print et aucune autre action.

    • PAS VRAI . -prune fonctionne avec n’importe quelle action sauf -delete . Pourquoi ça ne marche pas avec delete? Pour que -delete fonctionne, 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 que -prune logique, find doit bash un répertoire et arrêtez de le descendre, ce qui n’a clairement aucun sens avec -depth ou -delete on.

Performance

J’ai mis en place un test simple des trois meilleures réponses relevées sur cette question (remplacé -print par -exec bash -c 'echo $0' {} \; pour afficher 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

La syntaxe de f10bit et la syntaxe de Daniel C. Sobral prenaient en moyenne 10-25 ms. La syntaxe de GetFree , qui n’utilise pas -prune , a pris 865ms. Donc, oui, c’est un exemple assez extrême, mais si vous vous souciez de l’exécution et que vous faites quelque chose de très intensif, vous devez utiliser -prune .

Notez que la syntaxe de Daniel C. Sobral a été la meilleure des syntaxes à deux temps; mais, je soupçonne fortement que ceci est le résultat d’une mise en cache en tant que commutation de l’ordre dans lequel les deux lancés ont abouti au résultat opposé, tandis que la version non-pruneau é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=$((es)) 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=$((es)) 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=$((es)) printf " [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n" echo "Cleaning up test files..." cleanup 

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 

Je préfère la -not not … c’est plus lisible:

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

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”.

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

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

Je l’ai utilisé pour trouver tous les fichiers qui ne figurent pas dans les chemins “. *”:

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

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

Recherche de “NameOfFile” à l’exclusion de “Directory”. Mettre l’accent sur les écanvass *.

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

L’approche -path -prune fonctionne également avec des caractères génériques dans le chemin. Voici une instruction find qui trouvera les répertoires d’un serveur git desservant plusieurs repositorys git sans tenir compte des répertoires internes de git:

 find . -type d \ -not \( -path */objects -prune \) \ -not \( -path */twigs -prune \) \ -not \( -path */refs -prune \) \ -not \( -path */logs -prune \) \ -not \( -path */.git -prune \) \ -not \( -path */info -prune \) \ -not \( -path */hooks -prune \) 

-prune fonctionne définitivement et est la meilleure réponse car elle empêche de descendre dans le répertoire que vous souhaitez exclure. -not -path qui recherche toujours le -not -path exclu, il n’imprime simplement pas le résultat, ce qui peut poser problème si le répertoire exclu est monté sur le volume réseau ou si vous ne disposez pas d’permissions.

La partie la plus délicate est que find est très particulier quant à l’ordre des arguments, donc si vous ne les obtenez pas correctement, votre commande pourrait ne pas fonctionner. L’ordre des arguments est généralement tel que:

 find {path} {options} {action} 

{path} : Mettez d’abord tous les arguments liés au chemin, comme . -path './dir1' -prune -o . -path './dir1' -prune -o

{options} : J’ai le plus de succès lorsque vous -name, -iname, etc comme dernière option de ce groupe. Par exemple -type f -iname '*.js'

{action} : vous souhaitez append -print lors de l’utilisation de -prune

Voici un exemple pratique:

 # setup test mkdir dir1 dir2 dir3 touch dir1/file.txt; touch dir1/file.js touch dir2/file.txt; touch dir2/file.js touch dir3/file.txt; touch dir3/file.js # search for *.js, exclude dir1 find . -path './dir1' -prune -o -type f -iname '*.js' -print # search for *.js, exclude dir1 and dir2 find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print 

Pour une solution de travail (testée sur Ubuntu 12.04 (Precise Pangolin)) …

 find ! -path "dir1" -iname "*.mp3" 

recherchera des fichiers MP3 dans le dossier et les sous-dossiers actuels, sauf dans le sous-dossier dir1.

Utilisation:

 find ! -path "dir1" ! -path "dir2" -iname "*.mp3" 

… pour exclure dir1 AND dir2

Pour exclure plusieurs répertoires:

 find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \) 

Pour append des répertoires, ajoutez -o -path "./dirname/*" :

 find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\) 

Mais peut-être devriez-vous utiliser une expression régulière , s’il y a beaucoup de répertoires à exclure.

Vous pouvez utiliser l’option d’élagage pour y parvenir. Comme par exemple:

 find ./ -path ./beta/* -prune -o -iname example.com -print 

Ou l’option “grep -v” de grep inverse:

 find -iname example.com | grep -v beta 

Vous pouvez trouver des instructions détaillées et des exemples dans la commande de recherche d’exclusion des répertoires Linux .

Il y a beaucoup de bonnes réponses, cela m’a pris juste un peu de temps pour comprendre à quoi servaient chaque élément de la commande et la logique qui la sous-tendait.

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

find va commencer à trouver des fichiers et des répertoires dans le répertoire en cours, d’où la find . .

L’option -o représente un OU logique et sépare les deux parties de la commande:

 [ -path ./misc -prune ] OR [ -name '*.txt' -print ] 

Tout répertoire ou fichier qui n’est pas le répertoire ./misc ne réussira pas le premier test -path ./misc . Mais ils seront testés par rapport à la seconde expression. Si leur nom correspond au modèle *.txt ils sont imprimés à cause de l’option -print .

Lorsque find atteint le répertoire ./misc, ce répertoire ne satisfait que la première expression. Donc, l’option -prune sera appliquée. Il indique à la commande find de ne pas explorer ce répertoire. Ainsi, tout fichier ou répertoire dans ./misc ne sera même pas exploré par find, ne sera pas testé sur la deuxième partie de l’expression et ne sera pas imprimé.

J’utilisais find pour fournir une liste de fichiers pour xgettext , et xgettext voulais omettre un répertoire spécifique et son contenu. J’ai essayé beaucoup de permutations de -path combinées avec -prune mais je n’ai pas pu exclure complètement le répertoire que je voulais partir.

Bien que j’aie pu ignorer le contenu du répertoire que je voulais ignorer, find renvoyé le répertoire lui-même parmi les résultats, ce qui a entraîné le xgettext de xgettext (n’accepte pas les répertoires, mais uniquement les fichiers).

Ma solution était d’utiliser simplement grep -v pour ignorer le répertoire que je ne voulais pas dans les résultats:

 find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext 

Qu’il y ait ou non un argument pour find qui fonctionnera à 100%, je ne peux pas dire avec certitude. L’utilisation de grep était une solution rapide et facile après quelques maux de tête.

 find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*' 

semble fonctionner de la même manière que

 find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \) 

et est plus facile à retenir IMO.

  find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune 

Aucune des réponses précédentes n’est bonne sur Ubuntu. Essaye ça:

 find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*" 

J’ai trouvé ça ici

Ceci me convient sur Mac:

 find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune 

Il va exclure vendor répertoires vendor et app/cache pour le nom de recherche qui a suffixé avec php .

Pour ceux d’entre vous sur les anciennes versions d’UNIX qui ne peuvent pas utiliser -path ou -not

Testé sur SunOS 5.10 bash 3.2 et SunOS 5.11 bash 4.4

 find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f 

Comment utiliser-prune-option-of-find-in-sh est une excellente réponse de Laurence Gonsalves sur le fonctionnement de -prune .

Et voici la solution générique:

 find /path/to/search \ -type d \ \( -path /path/to/search/exclude_me \ -o \ -name exclude_me_too_anywhere \ \) \ -prune \ -o \ -type f -name '*\.js' -print 

Pour éviter de taper /path/to/seach/ plusieurs fois, pushd .. popd la find dans une paire pushd .. popd .

 pushd /path/to/search; \ find . \ -type d \ \( -path ./exclude_me \ -o \ -name exclude_me_too_anywhere \ \) \ -prune \ -o \ -type f -name '*\.js' -print; \ popd 

J’ai trouvé le nom des fonctions dans les fichiers de sources C exclure * .o et les exclure * .swp et exclude (pas un fichier régulier) et exclure la sortie dir avec cette commande:

 find . \( ! -path "./output/*" \) -a \( -type f \) -a \( ! -name '*.o' \) -a \( ! -name '*.swp' \) | xargs grep -n soc_attach 

Mieux vaut utiliser l’action exec que la boucle for :

 find . -path "./dirtoexclude" -prune \ -o -exec java -jar config/yuicompressor-2.4.2.jar --type js '{}' -o '{}' \; 

L’ exec ... '{}' ... '{}' \; sera exécuté une fois pour chaque fichier correspondant, en remplaçant les accolades '{}' par le nom de fichier actuel.

Notez que les accolades sont placées entre guillemets pour les protéger de l’interprétation en tant que ponctuation du script shell * .


Remarques

* De la section find (GNU findutils) 4.4.2 page de manuel find (GNU findutils) 4.4.2

J’ai essayé la commande ci-dessus, mais aucun de ceux qui utilisent “-prune” ne fonctionne pour moi. Finalement, j’ai essayé ceci avec la commande ci-dessous:

 find . \( -name "*" \) -prune -a ! -name "directory" 

Cela fonctionne car find les fichiers TESTS pour le modèle* foo * “:

 find ! -path "dir1" ! -path "dir2" -name "*foo*" 

mais cela ne fonctionne PAS si vous n’utilisez pas un motif ( find ne teste PAS le fichier). Donc, find ne fait pas usage de ses anciens ” vrais ” et ” faux ” bools évalués. Exemple pour ne pas utiliser le cas d’utilisation avec la notation ci-dessus:

 find ! -path "dir1" ! -path "dir2" -type f 

Il n’y a pas de test! Donc, si vous avez besoin de trouver des fichiers sans aucune correspondance de modèle, utilisez -prune. En outre, l’utilisation de prune find est toujours plus rapide alors qu’elle ignore vraiment ces répertoires au lieu de la faire correspondre ou de ne pas la faire correspondre. Donc, dans ce cas, utilisez quelque chose comme:

 find dir -not \( -path "dir1" -prune \) -not \( -path "dir2" -prune \) -type f 

ou:

 find dir -not \( -path "dir1" -o -path "dir2" -prune \) -type f 

Cordialement

Pour les utilisateurs de FreeBSD :

  find . -name '*.js' -not -path '*exclude/this/dir*' 

Si les répertoires de recherche ont un motif (dans mon cas la plupart du temps); vous pouvez simplement le faire comme ci-dessous:

 find ./n* -name "*.tcl" 

Dans l’exemple ci-dessus il recherche dans tous les sous-répertoires commençant par “n”.

J’ai trouvé les suggestions sur cette page et beaucoup d’autres pages ne fonctionnent tout simplement pas sur mon système Mac OS X. Cependant, j’ai trouvé une variante qui fonctionne pour moi.

L’idée majeure est de rechercher le Macintosh HD mais d’éviter de parcourir tous les volumes externes, qui sont principalement des sauvegardes Time Machine, des sauvegardes d’images, des partages montés et des archives, mais sans les démonter, ce qui est souvent peu pratique.

Voici mon script de travail, que j’ai nommé “findit”.

 #!/usr/bin/env bash # inspired by http://stackoverflow.com/questions/4210042/exclude-directory-from-find-command Danile C. Sobral # using special syntax to avoid traversing. # However, logic is refactored because the Sobral version still traverses # everything on my system echo ============================ echo find - from cwd, omitting external volumes date echo Enter sudo password if requested sudo find . -not \( \ -path ./Volumes/Archive -prune -o \ -path ./Volumes/Boot\ OS\ X -prune -o \ -path ./Volumes/C \ -path ./Volumes/Data -prune -o \ -path ./Volumes/jas -prune -o \ -path ./Volumes/Recovery\ HD -prune -o \ -path ./Volumes/Time\ Machine\ Backups -prune -o \ -path ./Volumes/SuperDuper\ Image -prune -o \ -path ./Volumes/userland -prune \ \) -name "$1" -print date echo ============================ iMac2:~ jas$ 

Les différents chemins d’access concernent les volumes d’archives externes, Time Machine, les machines virtuelles, les autres serveurs montés, etc. Certains noms de volumes contiennent des espaces.

Un bon test est “findit index.php”, car ce fichier se trouve à de nombreux endroits de mon système. Avec ce script, il faut environ 10 minutes pour rechercher le disque dur principal. Sans ces exclusions, cela prend de nombreuses heures.

Je voulais connaître le nombre de répertoires, classer un MB uniquement dans le répertoire actuel – et ce code fait exactement ce que je veux 🙂

la source

 - ... 2791037 Jun 2 2011 foo.jpg - ... 1284734651 Mär 10 16:16 foo.tar.gz - ... 0 Mär 10 15:28 foo.txt d ... 4096 Mär 3 17:12 HE d ... 4096 Mär 3 17:21 KU d ... 4096 Mär 3 17:17 LE d ... 0 Mär 3 17:14 NO d ... 0 Mär 3 17:15 SE d ... 0 Mär 3 17:13 SP d ... 0 Mär 3 17:14 TE d ... 0 Mär 3 19:20 UN 

le code

 format="%s%'12d\n" find . -type d -not -path "./*/*" | wc -l | awk -v fmt=$format '{printf fmt, " Anzahl Ordner = ", $1-1}' find . -type f -not -path "./*/*" | wc -l | awk -v fmt=$format '{printf fmt, " Anzahl Dateien = ", $1}' du . -hmS --max-depth=0 | awk -v fmt=$format '{printf fmt, " Groesse (MB) = ", $1}' 

note: le format="%s%'12d\n" supplémentaire format="%s%'12d\n" est nécessaire pour que awk les nombres.

le résultat

 Anzahl Ordner = 8 Anzahl Dateien = 3 Groesse (MB) = 1.228