Comment exclure un répertoire dans find. commander


1380

J'essaye de lancer un find commande pour tous les fichiers JavaScript, mais comment exclure un répertoire spécifique?

Voici le findcode que nous utilisons.

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

10
Quel est le répertoire que vous devez exclure?
L'Archétype Paul

11
Il vaut mieux l'utiliser find ... | while read -r file .... En outre, il vaut mieux accepter et voter positivement.
pause jusqu'à nouvel ordre.

tandis que la lecture est lente, car en est plus rapide
mpapis

18
@mpapis pendant la lecture gère correctement les lignes complètes avec des espaces.
Jean-Philippe Pellet

1
Il suffit de lancer ce dans un dossier contenant des fichiers avec des espaces dans leurs noms: for file in $(find .); do echo "$file"; done. Les noms avec des espaces sont divisés, ce que nous ne voulons pas.
Jean-Philippe Pellet

Réponses:


1140

Utilisez l' -pruneinterrupteur. Par exemple, si vous souhaitez exclure le miscrépertoire, ajoutez simplement un -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

Nous excluons ici dir1 , dir2 et rep3 , puisque dans les findexpressions est une action qui agit sur les critères -path dir1 -o -path dir2 -o -path dir3(si dir1 ou dir2 ou rep3 ), avec ANDED type -d.

Une autre action consiste -o printà imprimer.


89
Hmm. Cela ne fonctionne pas non plus pour moi car il inclura le répertoire ignoré "./misc" dans la sortie.
Theuni

84
@Theuni Cela n'a probablement pas fonctionné pour vous car vous n'avez pas ajouté -printexplicitement de (ou toute autre action) après -name. Dans ce cas, les deux "côtés" de l' -oimpression finissent, alors que si vous utilisez -print, seul ce côté s'imprime.
Daniel C.Sobral

4
De la page de manuel: Because -delete implies -depth, you cannot usefully use -prune and -delete together.Alors, comment puis-je supprimer avec find si je souhaite exclure des répertoires spécifiques de la suppression?
Jānis Elmeris

15
Pour supprimer tout le répertoire lui - même de l'utilisation des résultats: find . -not -path "./.git*". Utiliser ./dir*au lieu de ./dir/*supprime le répertoire ainsi que le contenu de la sortie.
micahblu

64
Cette question et la confusion dans les réponses est manifeste sur la façon dont l'interface utilisateur de recherche correspond à ce dont les gens ont besoin.
Johannes Overmann

1932

Si -prunecela ne fonctionne pas pour vous, cela:

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

Avertissement: nécessite de parcourir tous les répertoires indésirables.


86
Un des commentaires de la réponse acceptée souligne le problème. -prunen'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.
GetFree

96
Très bonne réponse. J'ajouterais à cela que vous pouvez exclure un répertoire à TOUT niveau en remplaçant le premier .par *. find -name "*.js" -not -path "*/omitme/*"omettrait donc les fichiers d'un répertoire nommé "omitme" à n'importe quel niveau de profondeur.
DeeDee

83
Cependant, il traverse tout le répertoire indésirable. J'ajoute ma propre réponse. :-)
Daniel C. Sobral

18
Notez cependant que l'option prune ne fonctionne que si vous ne l'utilisez pas -printexplicitement.
Daniel

39
Il serait préférable de dire "C'est une alternative à l'utilisation de -prune". Les réponses suggérant -prune ne sont clairement pas fausses, elles ne sont tout simplement pas comme vous le feriez.
Jimbo

459

Je trouve les éléments suivants plus faciles à 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

Remarque importante: les chemins que vous tapez après -pathdoivent correspondre exactement à ce findqui s'imprimerait sans exclusion. Si cette phrase vous embrouille, assurez-vous simplement d'utiliser des chemins complets à travers toute la commande comme ceci:find /full/path/ -not \( -path /full/path/exclude/this -prune \) ... . Voir la note [1] si vous souhaitez une meilleure compréhension.

À l'intérieur \(et \)est une expression qui correspondra exactement build/external (voir la note importante ci-dessus) et, en cas de succès, évitera de traverser quoi que ce soit ci-dessous . Ceci est ensuite regroupé en une seule expression avec la parenthèse échappée, et préfixé avec -notqui ferafind sauter tout ce qui correspondait à cette expression.

On peut se demander si l'ajout -notne fera pas -pruneréapparaître tous les autres fichiers cachés , et la réponse est non. Le chemin-prune fonctionnement est que tout ce qui, une fois atteint, les fichiers en dessous de ce répertoire sont ignorés de façon permanente.

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


Remarque [1] : Si vous souhaitez exclure /tmp/foo/baret que vous exécutez find comme ceci " find /tmp \(...", vous devez le spécifier -path /tmp/foo/bar. Si d'un autre côté vous lancez find comme ceci, cd /tmp; find . \(...vous devez spécifier -path ./foo/bar.


37
Réponse exceptionnelle, merci. Cela fonctionne et est évolutif (lisible) pour plusieurs exclusions. Vous êtes un gentleman et un savant monsieur. Merci pour l'exemple pour plusieurs exclusions
Freedom_Ben

7
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.
Jānis Elmeris

17
@Janis Vous pouvez utiliser à la -exec rm -rf {} \;place de -delete.
Daniel C.Sobral

11
En examinant la sortie de find, c'est vraiment évident, mais ça m'a fait trébucher. Si vous êtes à la recherche dans le répertoire courant (en spécifiant .que le chemin de recherche, ou ne pas spécifier une), vous souhaiterez probablement votre modèle après -pathpour commencer ./, par exemple: find -not \( -path ./.git -prune \) -type f.
Zantier

7
Une variante plus précise (et compatible POSIX) de cette méthode: find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \)suivie de toutes les conditions qui devraient correspondre à ce que vous recherchez.
Walf

219

Il y a clairement une certaine confusion ici 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

Depuis la page de manuel GNU find

Raisonnement

-prunearrête findde descendre dans un répertoire. La simple spécification -not -pathdescendra toujours dans le répertoire ignoré , mais -not -pathsera fausse à chaque fois que findchaque fichier sera testé.

Problèmes avec -prune

-prune fait ce à quoi il est destiné, mais vous devez quand même prendre soin de certaines choses lorsque vous l'utilisez.

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

    • VRAI C'est le comportement prévu, il n'y descend tout simplement pas. Pour éviter d'imprimer complètement le répertoire, utilisez une syntaxe qui l'omet logiquement.
  2. -prunene fonctionne qu'avec -printet aucune autre action.

    • PAS VRAI . -prunefonctionne avec n'importe quelle action sauf -delete. Pourquoi cela ne fonctionne-t-il pas avec la suppression? Pour -deletetravailler, trouver doit parcourir le répertoire dans l'ordre DFS, car -deletesupprimera d'abord les feuilles, puis les parents des feuilles, etc ... Mais pour spécifier -prunepour avoir du sens, il findfaut frapper un répertoire et arrêter de le descendre, ce qui n'a clairement aucun sens avec -depthou -deletesur.

Performance

J'ai mis en place un test simple des trois réponses les plus votées sur cette question (remplacé -printpar -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

Les deux la syntaxe de f10bit et la syntaxe de Daniel C. Sobral a pris 10-25ms à courir en moyenne. La syntaxe de GetFree , qui ne l'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'intensif à distance, vous devez utiliser -prune.

Remarquez que la syntaxe de Daniel C. Sobral a donné le meilleur des deux -prunesyntaxes; mais, je soupçonne fortement que cela est le résultat d'une mise en cache car le changement de l'ordre dans lequel les deux s'exécutaient a abouti au résultat opposé, tandis que la version non-prune é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

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

Après quelques tests sur ces deux avec -pruneje peux dire qu'il y a rarement de différence. Gardez à l'esprit que la première commande qui bénéficiera des performances du processeur, le réchauffement du processeur ultérieur> la baisse des performances entraînera un ralentissement mineur (j'ai purgé le cache avant chaque commande en tant que suggestion @ndemou)
Huy.PhamNhu

Essayez de changer le numéro parmi le name1() name2() name3()script de test @BroSlow ci-dessus pour changer l'ordre d'exécution pour obtenir un visuel sur ce que j'ai dit. Dans la vraie vie, il est cependant imperceptible entre ces deux-là.
Huy.PhamNhu

Applaudissements. Merci pour cette réponse de qualité.
Stéphane

Vous ne devriez pas être -o ce qui signifie ou. vous devez donc tailler dans la première étape, puis oublier tout cela dans la prochaine.
mmm

97

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

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

Recherche de "MyFile" à l'exclusion de "Directory". Mettez l'accent sur les étoiles *.


13
Cette méthode fonctionne sur macOS, contrairement à la réponse acceptée. Je sais que la question d'origine est pour Linux.
Xavier Rubio Jansana

5
Notez que vous pouvez ajouter plusieurs ! -path '*/Directory/*'à votre commande successivement pour ignorer plusieurs répertoires
Aclwitt

Fonctionne sur MacOS mais pas sur Linux ... confirmé
Marcello de Sales

Dans un docker containerseul travail avecsh -c "find..."
Marcello de Sales

@Marcello de Sales Bien sûr, cela fonctionne sous Linux.
DimiDak

59

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
Cela rendra votre recherche très lente
Dorian

6
Celui-ci a fonctionné pour moi, d'autres (qui utilisent -prune) - ne fonctionne pas.
Andron

7
Lent dans les grands résultats, mais utile dans les petits ensembles. Mais comment exclure plusieurs répertoires à l'aide de grep? Bien sûr, de cette façon: find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3mais il peut y avoir une méthode de grep.
Timo Kähkönen

6
Si vous souhaitez effectuer plusieurs greps alors vous seriez mieux l'écrire comme des expressions régulières: egrep -v '(dir1|dir2|dir3)'. Cependant, dans cette étude de cas spécifique, il serait préférable d'exclure les répertoires en findsoi.
Laurence

1
oui, et vous n'avez pas besoin de parenthèses et il serait préférable d'utiliser ^ pour vous assurer qu'il correspond au nom du répertoire au début de la chaîne, par exemple: find. -name '* .js' | egrep -v "^ \ ./ exclusdir1 | ^ \ ./ exclusdir2"
Sofija

41

Je préfère la -notnotation ... c'est plus lisible:

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

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

8
C'est faux. Cela n'empêche pas find d'entrer dans le répertoire et de parcourir tous les fichiers à l'intérieur.
GetFree

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

Cela m'a aidé avec le chemin .git find . -iname '*' -not -path './.git/*'
Mark Shust à M.academy

7
@rane: Plus précisément find . -not -path "*/.git*"serait ce que vous voulez.
Ben

20

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

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

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


1
C'est la seule solution pure «trouver» qui a fonctionné pour moi. Les répertoires que je souhaitais exclure ne sont PAS immédiatement en dessous du répertoire de travail actuel.
Lambart

5
Cependant, l'ajout -printà la fin peut améliorer les résultats. find . -type d -name .hg -prune -o -name dataignoré le contenu des (multiples) .hgrépertoires, mais répertorié les .hgrépertoires eux-mêmes. Avec -print, il ne listait que les répertoires "data" que je cherchais.
Lambart

19

-prunefonctionne certainement et est la meilleure réponse car elle empêche de descendre dans le répertoire que vous souhaitez exclure. -not -pathqui recherche toujours le répertoire exclu, il n'imprime tout simplement pas le résultat, ce qui pourrait être un problème si le répertoire exclu est monté sur le volume réseau ou si vous n'avez pas d'autorisations.

La partie délicate est qui findest très particulière sur l'ordre des arguments, donc si vous ne les obtenez pas correctement, votre commande peut ne pas fonctionner. L'ordre des arguments est généralement le suivant:

find {path} {options} {action}

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

{options}: J'ai le plus de succès en mettant -name, -iname, etccomme dernière option dans ce groupe. Par exemple-type f -iname '*.js'

{action}: Vous voudrez ajouter -printlors de l'utilisation-prune

Voici un exemple de travail:

# 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

16

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 se trouvent pas dans les chemins ". *":

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

J'ai essayé cela et il descend toujours dans les répertoires, donc la vitesse n'est certainement pas améliorée.
Br.Bill

10

L'approche -path -prune fonctionne également avec les caractères génériques dans le chemin. Voici une instruction find qui trouvera les répertoires d'un serveur git desservant plusieurs référentiels git en laissant de côté les répertoires internes git:

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

9

Pour exclure plusieurs répertoires:

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

Pour ajouter 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.


9

Il y a beaucoup de bonnes réponses, il m'a juste fallu un certain temps pour comprendre à quoi servait chaque élément de la commande et la logique derrière elle.

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

find commencera à rechercher des fichiers et des répertoires dans le répertoire courant, d'où find . .

L' -ooption 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 passera pas le premier test -path ./misc. Mais ils seront testés par rapport à la deuxième expression. Si leur nom correspond au motif *.txtimprimé, à cause de la-print option.

Lorsque find atteint le répertoire ./misc, ce répertoire ne satisfait que la première expression. L' -pruneoption lui sera donc appliquée. Il indique à la commande find de ne pas explorer ce répertoire. Ainsi, aucun fichier ou répertoire dans ./misc ne sera même exploré par find, ne sera pas testé par rapport à la deuxième partie de l'expression et ne sera pas imprimé.


Tout le monde a une solution, mais la vôtre l'explique le mieux. J'étais catégorique pour que -name soit utilisé en premier plutôt que -path. Votre explication était suffisante pour arriver à ce que je voulais. trouver . -name "* .txt" -print -o -path ./misc -prune
Vendetta V

7

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

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

recherchera les 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 ET dir2


Ça ne marche pas pour moi. Aucune des réponses ci-dessus non plus. Chapeau rouge.
Tharpa

6

une bonne astuce pour éviter d'imprimer les répertoires élagués est d'utiliser -print(fonctionne -execaussi) après le côté droit de l' -oraprès -prune. Par exemple, ...

find . -path "*/.*" -prune -or -iname "*.j2"

imprimera le chemin de tous les fichiers sous le répertoire actuel avec l'extension `.j2", en ignorant tous les répertoires cachés. Neat. ne suit pas, ...

find . -path "*/.*" -prune -or -iname "*.j2" -print

car logiquement, il y a un caché -andaprès l' -inameopérateur et avant l'impression. Cela le lie à la partie droite de la -orclause en raison de l'ordre booléen des opérations et de l'associativité. Mais les docs disent qu'il y a un caché -prints'il (ou l'un de ses cousins ​​... -print0, etc.) n'est pas spécifié. Alors pourquoi la partie gauche de l' -orimpression n'est-elle pas ? Apparemment (et je n'ai pas compris cela lors de ma première lecture de la page de manuel), c'est vrai s'il n'y en a pas avec uniquement des opérateurs descriptifs ne ferait apparemment rien, donc je suppose que cela a du sens. Comme mentionné ci-dessus, tout cela fonctionne également, donc ce qui suit donne une liste complète pour chaque fichier avec l'extension souhaitée, mais pas la liste du premier niveau de chaque répertoire caché, ...-print -ou -execPARTOUT, auquel cas, -print est logiquement saupoudré de telle sorte que tout soit imprimé. Si même UNprint opération de style dans une clause, toutes ces logiques cachées disparaissent et vous obtenez seulement ce que vous spécifiez. Franchement, j'aurais peut-être préféré l'inverse, mais unfind-execls -la

find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +

Pour moi (et d'autres sur ce fil), la findsyntaxe devient assez baroque assez rapidement, donc je jette toujours des parenthèses pour être sûr que je sais ce qui se lie à quoi, donc je crée généralement une macro pour la capacité de frappe et forme toutes ces déclarations comme. ..

find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
\( ... description of stuff I want to find ... [ -exec or -print] \)

Il est difficile de se tromper en configurant le monde en deux parties de cette façon. J'espère que cela aide, bien qu'il semble peu probable que quiconque lise la 30 + e réponse et la vote, mais on peut espérer. :-)


5

Vous pouvez utiliser l'option prune pour y parvenir. Comme par exemple:

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

Ou l'option grep inverse "grep -v":

find -iname example.com | grep -v beta

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


La solution grep est la seule qui exclut tous les répertoires du même nom. Lorsque vous essayez d'exclure "node_modules", cela est très utile.
bmacnaughton

3
@bmacnaughton - pas vrai! Je suis venu ici en cherchant spécifiquement à exclure "node_modules" et après avoir lu de nombreuses bonnes réponses, je me suis installé find . -type f -print -o -path "*/node_modules" -prune... en utilisant le caractère générique, cela saute "node_modules" à n'importe quel niveau; l'utilisation -printde la première alternative -type f -printne fait qu'imprimer cette partie, donc les répertoires "node_modules" eux-mêmes ne sont pas répertoriés. (il peut aussi être inversé: find . -path "*/node_modules" -prune -o -type f -print)
Stephen P

qu'est-ce que * / fait là-bas. Quel est le fichier exact que vous souhaitez exclure. Ypu l'utilise-t-il en tant que caractère générique?
Siju V

1
@StephenP, merci de l'avoir signalé; J'ai appris la différence entre utiliser ./node_moduleset en tirer */node_modulesparti. Pour mon cas, où node_modulesn'existe que dans le répertoire dans lequel je lance la recherche (et sous ce node_modulesrépertoire), je peux l'utiliser find . -type f -print -o -path "./node_modules" -prune car il n'y aura pas de node_modulesrépertoire sous aucun autre répertoire.
bmacnaughton

1
@SijuV - dans le répertoire où je cherchais, il y avait un node_modulessous - répertoire, mais il y avait aussi des sous-répertoires qui avaient leurs propres nœuds_modules ... utilisant ./node_modulesuniquement les correspondances du sous-répertoire du répertoire node_modulesen cours .et les élaguant; l'utilisation */node_modulescorrespond et élague le répertoire à n'importe quelle profondeur, car le *as a glob correspond à tout préfixe de chemin principal, tel que ./test5/main/node_modules, non seulement le ./préfixe. Le *est un caractère générique, mais en tant que glob et non en tant que regex.
Stephen P

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

Impossible de faire fonctionner celui-ci. find ~/Projects -name '*.js' -\! -name 'node_modules' -prunetourne toujours des fichiers avec node_modulessur son chemin
mpen

1
@mpen, De stackoverflow.com/questions/4210042/… , j'ai appris que la syntaxe que vous voulez est find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print. Le nom de ce chemin doit correspondre exactement à ce que la recherche imprimerait si elle devait imprimer le répertoire.
PatS

4
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 il est plus facile de se souvenir de l'OMI.


4

TLDR: comprenez vos répertoires racine et personnalisez votre recherche à partir de là, en utilisant l' -path <excluded_path> -prune -ooption. Ne pas inclure de fin/ à la fin du chemin exclu.

Exemple:

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print


Pour utiliser efficacement le findje pense qu'il est impératif d'avoir une bonne compréhension de la structure de répertoire de votre système de fichiers. Sur mon ordinateur personnel, j'ai des disques durs multi-To, avec environ la moitié de ce contenu sauvegardé à l'aide rsnapshot(c'est-à-dire rsync). Bien que la sauvegarde sur un lecteur physiquement indépendant (en double), il est monté sous mon /répertoire racine système ( ) /mnt/Backups/rsnapshot_backups/:

/mnt/Backups/
└── rsnapshot_backups/
    ├── hourly.0/
    ├── hourly.1/
    ├── ...
    ├── daily.0/
    ├── daily.1/
    ├── ...
    ├── weekly.0/
    ├── weekly.1/
    ├── ...
    ├── monthly.0/
    ├── monthly.1/
    └── ...

Le /mnt/Backups/rsnapshot_backups/répertoire occupe actuellement environ 2,9 To, avec environ 60 millions de fichiers et dossiers; traverser simplement ces contenus prend du temps:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find /mnt/Backups/rsnapshot_backups | wc -l
60314138    ## 60.3M files, folders
34:07.30    ## 34 min

time du /mnt/Backups/rsnapshot_backups -d 0
3112240160  /mnt/Backups/rsnapshot_backups    ## 3.1 TB
33:51.88    ## 34 min

time rsnapshot du    ## << more accurate re: rsnapshot footprint
2.9T    /mnt/Backups/rsnapshot_backups/hourly.0/
4.1G    /mnt/Backups/rsnapshot_backups/hourly.1/
...
4.7G    /mnt/Backups/rsnapshot_backups/weekly.3/
2.9T    total    ## 2.9 TB, per sudo rsnapshot du (more accurate)
2:34:54          ## 2 hr 35 min

Ainsi, chaque fois que je dois rechercher un fichier sur ma /partition (racine), je dois gérer (éviter si possible) la traversée de ma partition de sauvegarde.


EXEMPLES

Parmi les différentes approches suggérées dans ce fil ( Comment exclure un répertoire dans la commande find. ), Je trouve que les recherches en utilisant la réponse acceptée sont beaucoup plus rapides - avec des mises en garde.

Solution 1

Disons que je veux trouver le fichier système libname-server-2.a, mais je ne veux pas parcourir mes rsnapshotsauvegardes. Pour trouver rapidement un fichier système, utilisez le chemin d'exclusion /mnt(c'est-à-dire, utilisez /mnt, pas /mnt/, ou /mnt/Backups, ou ...):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a
real    0m8.644s              ## 8.6 sec  <<< NOTE!
user    0m1.669s
 sys    0m2.466s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 3 sec     ## ~3 sec  <<< NOTE!

... trouve ce fichier en quelques secondes, alors que cela prend beaucoup plus de temps (apparaissant comme récurrent dans tous les répertoires "exclus"):

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -path /mnt/ -prune -o -name "*libname-server-2.a*" -print
find: warning: -path /mnt/ will not match anything because it ends with /.
/usr/lib/libname-server-2.a
real    33m10.658s            ## 33 min 11 sec (~231-663x slower!)
user    1m43.142s
 sys    2m22.666s

## As regular user (victoria); I also use an alternate timing mechanism, as
## here I am using 2>/dev/null to suppress "Permission denied" warnings:

$ START="$(date +"%s")" && find 2>/dev/null / -path /mnt/ -prune -o \
    -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
    TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/usr/lib/libname-server-2.a
find command took 1775 sec    ## 29.6 min

Solution 2

L'autre solution proposée dans ce fil ( SO # 4210042 ) fonctionne également mal:

## As sudo (#), to avoid numerous "Permission denied" warnings:

time find / -name "*libname-server-2.a*" -not -path "/mnt"
/usr/lib/libname-server-2.a
real    33m37.911s            ## 33 min 38 sec (~235x slower)
user    1m45.134s
 sys    2m31.846s

time find / -name "*libname-server-2.a*" -not -path "/mnt/*"
/usr/lib/libname-server-2.a
real    33m11.208s            ## 33 min 11 sec
user    1m22.185s
 sys    2m29.962s

RÉSUMÉ | CONCLUSIONS

Utilisez l'approche illustrée dans la " Solution 1 "

find / -path /mnt -prune -o -name "*libname-server-2.a*" -print

c'est à dire

... -path <excluded_path> -prune -o ...

notant que chaque fois que vous ajoutez la fin /au chemin exclu, la findcommande entre alors récursivement (tous ces) /mnt/*répertoires - qui dans mon cas, en raison des /mnt/Backups/rsnapshot_backups/*sous - répertoires, comprend en outre ~ 2,9 To de fichiers à rechercher! En n'ajoutant pas de fin/ la recherche devrait se terminer presque immédiatement (en quelques secondes).

La "Solution 2" ( ... -not -path <exclude path> ...) semble également rechercher récursivement dans les répertoires exclus - ne renvoyant pas les correspondances exclues, mais consommant inutilement ce temps de recherche.


Recherche dans ces rsnapshot sauvegardes:

Pour rechercher un fichier dans l'une de mes rsnapshotsauvegardes horaires / quotidiennes / hebdomadaires / mensuelles ):

$ START="$(date +"%s")" && find 2>/dev/null /mnt/Backups/rsnapshot_backups/daily.0 -name '*04t8ugijrlkj.jpg'; END="$(date +"%s")"; TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
/mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg
find command took 312 sec   ## 5.2 minutes: despite apparent rsnapshot size
                            ## (~4 GB), it is in fact searching through ~2.9 TB)

Exclusion d'un répertoire imbriqué:

Ici, je veux exclure un répertoire imbriqué, par exemple /mnt/Vancouver/projects/ie/claws/data/*lors de la recherche à partir de /mnt/Vancouver/projects/:

$ time find . -iname '*test_file*'
./ie/claws/data/test_file
./ie/claws/test_file
0:01.97

$ time find . -path '*/data' -prune -o -iname '*test_file*' -print
./ie/claws/test_file
0:00.07

En plus: l' ajout -printà la fin de la commande supprime l'impression du répertoire exclu:

$ find / -path /mnt -prune -o -name "*libname-server-2.a*"
/mnt
/usr/lib/libname-server-2.a

$ find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
/usr/lib/libname-server-2.a

Ce n'est pas la taille des fichiers qui ralentit find, c'est le nombre d'entrées de répertoire qu'il doit examiner. C'est donc bien pire si vous avez beaucoup, beaucoup de petits fichiers (surtout s'ils sont tous liés par plusieurs!) Que si vous n'avez qu'une poignée de fichiers de plusieurs gigaoctets.
Toby Speight

@TobySpeight: bon point. J'ai mentionné la taille de l'espace de recherche pour indiquer l'échelle, qui contient également de nombreux fichiers. Une recherche rapide de racine (/) avec sudo ls -R / | wc -lindique ~ 76,5 millions de fichiers (dont la plupart sont sauvegardés sauf les fichiers système "non config"); /mnt/Vancouver/avec ls -R | wc -lindique ~ 2,35 millions de fichiers; /home/victoria/contient 0,668 million de fichiers.
Victoria Stuart

4

Vous pouvez également utiliser des expressions régulières pour inclure / exclure certains fichiers / répertoires de votre recherche en utilisant quelque chose comme ceci:

find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*" 

Cela ne vous donnera que tous les fichiers js, vue, css, etc. mais en excluant tous les fichiers dans les dossiers node_moduleset vendor.


3

J'utilisais findpour fournir une liste de fichiers pour xgettext, et je voulais omettre un répertoire spécifique et son contenu. J'ai essayé de nombreuses permutations de -pathcombiné avec-prune mais pas pu exclure complètement le répertoire que je voulais quitter.

Bien que j'aie pu ignorer le contenu du répertoire que je voulais ignorer, j'ai findrenvoyé le répertoire lui-même comme l'un des résultats, ce qui a causéxgettext crash en conséquence (n'accepte pas les répertoires; uniquement les fichiers).

Ma solution était de simplement utiliser grep -vpour 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 findque cela fonctionne à 100%, je ne peux pas le dire avec certitude. L'utilisation grepétait une solution rapide et facile après quelques maux de tête.


3

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*"

Je l'ai trouvé ici


Je ne vois aucune raison pour laquelle aucune des réponses avec plus de 100 points ne devrait fonctionner sur Ubuntu.
Axel Beckert

mmm voyons? peut-être parce que je les ai tous essayés?
sixro

find est partout la même implémentation sur toutes les distributions Linux - celle du projet GNU. La seule différence pourrait être les versions. Mais les changements de la dernière décennie n'ont pas été aussi invasifs, sauf peut-être pour la correspondance des autorisations.
Axel Beckert

3

Cela me convient sur un Mac:

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

Il exclura vendoret dira le app/cachenom de recherche avec le suffixe php.


Mieux vaut mettre des guillemets simples autour de '* .php' ou vous ne trouverez pas ce que vous cherchez.
Br.Bill

3

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

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

Pourrait passer plus que le répertoire spécifié.
MUY Belgique

2

how-to-use-prune-option-of-find-in-sh est une excellente réponse de Laurence Gonsalves sur le -prunefonctionnement.

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, enveloppez-les finddans une pushd .. popdpaire.

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

1
De stackoverflow.com/questions/4210042/… , j'ai appris que la syntaxe utilisée pour le -pathdoit correspondre au nom que find imprimerait s'il devait imprimer le répertoire, par exemple find . -path ./.git -prune -o -print, ou find $HOME/foo -path $HOME/foo/.git -prune -o -print certaines des réponses disent simplement -path somedirqui est malheureusement pas assez précis pour être utile.
PatS

2

Pour ce dont j'avais besoin, cela fonctionnait comme ceci, en trouvant landscape.jpgdans tous les serveurs à partir de la racine et en excluant la recherche dans le /varrépertoire:

find / -maxdepth 1 -type d | grep -v /var | xargs -I '{}' find '{}' -name landscape.jpg

find / -maxdepth 1 -type drépertorie tous les d irectories en/

grep -v /var exclut `/ var 'de la liste

xargs -I '{}' find '{}' -name landscape.jpgexécuter n'importe quelle commande, comme findavec chaque répertoire / résultat de la liste


Attendez une seconde, /n'est pas encore exclu. Vous pourriez avoir besoin sed 1d.
Simba

2

Les commandes suivantes fonctionnent:

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

Si vous rencontrez un problème avec la recherche, utilisez l' -D treeoption pour afficher les informations d'analyse d'expression.

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

Ou le -D all, pour voir toutes les informations d'exécution.

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

1

J'ai trouvé le nom des fonctions dans les fichiers sources C exclure * .o et exclure * .swp et exclude (fichier non normal) 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

1

Mieux vaut utiliser l' execaction que la forboucle:

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

Le 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 simples pour les protéger de l'interprétation en tant que ponctuation de script shell * .


Remarques

* À partir de la section EXEMPLES de la find (GNU findutils) 4.4.2page de manuel


1
Très vieille question, mais avec encore des améliorations. Je l'ai trouvé par hasard en essayant de résoudre un problème similaire, et aucune des réponses n'était satisfaisante.
Alberto

J'utilise execsouvent l' action et la trouve très utile. J'ajoute généralement des guillemets entre {}dans le cas où il y a des espaces dans les chemins de fichier qui donne "{}".
Ludovic Kuty

@lkuty J'étais sur le point de modifier mon article pour refléter votre commentaire, mais après un test rapide (sans citer, {}fonctionne pour les fichiers avec des espaces dans leurs noms) et un regard dans les pages de manuel, il semble que la citation ne soit nécessaire que pour éviter qu'ils soient mal interprétés comme ponctuation du script shell. Dans ce cas, vous utiliseriez des guillemets simples:'{}'
Alberto

Je pense que je devais l'utiliser pour faire cpou mvou rm. Je vais le vérifier
Ludovic Kuty

1

J'ai essayé la commande ci-dessus, mais aucun de ceux qui utilisent "-prune" ne fonctionne pour moi. Finalement, j'ai essayé cela avec la commande ci-dessous:

find . \( -name "*" \) -prune -a ! -name "directory"
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.