Utilisez la commande find mais excluez les fichiers dans deux répertoires

86

Je souhaite rechercher les fichiers qui se terminent par _peaks.bed, mais excluent les fichiers des dossiers tmpet scripts.

Ma commande est comme ceci:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Mais ça n'a pas marché. Les fichiers dans tmpet le scriptdossier seront toujours affichés.

Quelqu'un a-t-il des idées à ce sujet?

Soleil de Hanfei
la source

Réponses:

189

Voici comment vous pouvez spécifier cela avec find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Explication:

  • find . - Lancer la recherche à partir du répertoire de travail actuel (récursivement par défaut)
  • -type f- Précisez findque vous ne voulez que des fichiers dans les résultats
  • -name "*_peaks.bed" - Recherchez les fichiers dont le nom se termine par _peaks.bed
  • ! -path "./tmp/*" - Exclure tous les résultats dont le chemin commence par ./tmp/
  • ! -path "./scripts/*" - Exclure également tous les résultats dont le chemin commence par ./scripts/

Tester la solution:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Vous étiez assez proche, l' -nameoption ne considère que le nom de base, où comme -pathconsidère le chemin complet =)

sampson-chen
la source
Bon travail. Cependant, vous avez oublié l'une des choses que l'OP voulait, pour trouver les fichiers se terminant par _peaks.bed.
alex
2
Cela utilise un certain nombre d'extensions dans GNU find, mais comme la question est étiquetée Linux, ce n'est pas un problème. Bonne réponse.
Jonathan Leffler
1
Une petite note: si vous utilisez .à votre invite de recherche initiale, vous devez l'utiliser dans chaque chemin que vous excluez. La correspondance de chemin est assez stricte, elle ne fait pas de recherche floue. Donc, si vous utilisez, cela find / -type f -name *.bed" ! -path "./tmp/"ne fonctionnera pas. vous devez le ! -path "/tmp"rendre heureux.
peelman
3
Il est important de noter que le * est important. $ ! -path "./directory/*"
Thomas Bennett
3
D'après les pages de manuel: "Pour ignorer toute une arborescence de répertoires, utilisez -pruneplutôt que de vérifier chaque fichier de l'arborescence." Si vos répertoires exclus fonctionnent très profondément ou contiennent des tonnes de fichiers et que vous vous souciez des performances, utilisez -pruneplutôt l' option.
thdoan
8

Voici une façon de le faire ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"
Alex
la source
2
Cela a le mérite de travailler avec n'importe quelle version de find, plutôt qu'avec uniquement GNU find. Cependant, la question est étiquetée Linux donc ce n'est pas critique.
Jonathan Leffler
2

Utilisation

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

ou

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

ou

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

L'ordre est important. Il évalue de gauche à droite. Commencez toujours par l'exclusion de chemin.

Explication

N'utilisez pas -not(ou !) pour exclure tout le répertoire. Utilisez -prune. Comme expliqué dans le manuel:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

et dans le manuel de recherche de GNU:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

En effet, si vous utilisez -not -path "./pathname", find évaluera l'expression pour chaque nœud sous"./pathname" .

Les expressions find ne sont que des évaluations de conditions.

  • \( \) - fonctionnement des groupes (vous pouvez utiliser -path "./tmp" -prune -o -path "./scripts" -prune -o , mais c'est plus verbeux).
  • -path "./script" -prune- si -pathretourne true et est un répertoire, renvoie true pour ce répertoire et ne le fait pas descend dedans.
  • -path "./script" ! -prune- il évalue comme (-path "./script") AND (! -prune). Cela change le "toujours vrai" du pruneau en toujours faux. Cela évite d'imprimer "./script"en correspondance.
  • -path "./script" -prune -false- puisque -prunerenvoie toujours vrai, vous pouvez le suivre avec -falsepour faire la même chose que !.
  • -o- Opérateur OR. Si aucun opérateur n'est spécifié entre deux expressions, la valeur par défaut est l'opérateur AND.

Par conséquent, \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printest étendu à:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

L'impression est ici importante car sans elle est étendue à:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printest ajouté par find - c'est pourquoi la plupart du temps, vous n'avez pas besoin de l'ajouter dans votre expression. Et puisque -prunerenvoie true, il affichera "./script" et "./tmp".

Ce n'est pas nécessaire dans les autres car nous avons basculé -prune pour toujours retourner faux.

Astuce: vous pouvez utiliser find -D opt expr 2>&1 1>/dev/nullpour voir comment il est optimisé et développé,
find -D search expr 2>&1 1>/dev/nullpour voir quel chemin est vérifié.

f380cédrique
la source
0

Essayez quelque chose comme

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

et ne soyez pas trop surpris si je me trompe un peu. Si l'objectif est un exécutable (au lieu d'imprimer), remplacez-le simplement.

DrC
la source
0

pour moi, cette solution n'a pas fonctionné sur un exécutable de commande avec find, je ne sais pas vraiment pourquoi, donc ma solution est

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Explication: identique à sampson-chen one avec les ajouts de

-prune - ignore le chemin de procédure de ...

-o - Ensuite, si aucune correspondance ne correspond, imprimez les résultats, (élaguez les répertoires et imprimez les résultats restants)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz
al3x2ndru
la source
La réponse acceptée n'a pas fonctionné, mais cela fonctionne. En utilisant pruneau, find . -path ./scripts -prune -name '*_peaks.bed' -type f. Je ne sais pas comment exclure plusieurs répertoires. Cela répertorie également le répertoire exclu de niveau supérieur même s'il typeest spécifié. Exclure via Grep semble plus simple à moins que vous ne souhaitiez utiliser prune pour accélérer l'opération de recherche.
Mohnish
J'ai également eu du mal à exclure plusieurs répertoires, mais les commentaires ci-dessus m'ont donné une réponse qui a fonctionné. J'utilise plusieurs instances de «-not -path» et dans chaque expression de chemin, j'inclus le préfixe complet tel qu'utilisé dans le premier paramètre pour «trouver» et terminer chacun par un astérisque (et échapper aux points).
jetset
0

Vous pouvez essayer ci-dessous:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'
Jacky Jiang
la source
2
Sur une vieille question comme celle-là (4 ans!), Vous voulez expliquer pourquoi cette nouvelle réponse est meilleure ou différente, pas seulement du code de "vidage".
Nic3500