Passer plusieurs répertoires à l'option -prune dans la recherche

9

J'utilise findpour localiser et supprimer des fichiers de sauvegarde, mais je souhaite exclure certains répertoires de la recherche. Les noms de fichiers de sauvegarde pourrait se terminer dans .bck, bak, ~ou backup.

Le code exemple de travail minimal (MWE) avec seulement trois répertoires à exclure est:

#! /bin/bash
find . -type d \( -path "./.*" -o -path "./Music" -o -path "./Documents" \) -prune -o -type f \( -name "*.bck" -o -name "*.bak" -o -name "*~" -o -name "*.backup" \) -print0 | xargs -0 --no-run-if-empty trash-put

La syntaxe \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prunesemble un peu maladroite, surtout s'il y a une dizaine de répertoires à exclure, même si je n'en ai montré que trois dans le MWE.

Existe-t-il une manière plus élégante d'utiliser soit un fichier d'entrée, avec la liste des répertoires exclus, soit une construction de type tableau ou liste, qui pourrait être mise en service?

Je suis désolé de ne pas avoir été plus explicite lorsque j'ai écrit ma question d'origine.

NB: trash-putest un utilitaire qui déplace les fichiers vers au Trashcanlieu de les supprimer [1].

[1]. https://github.com/andreafrancia/trash-cli

chandra
la source

Réponses:

4

Pour autant que je sache, il n'y a pas d'option pour dire findde lire les modèles d'un fichier. Une solution de contournement simple consiste à enregistrer les modèles que je souhaite exclure dans un fichier et à passer ce fichier en entrée pour une inversion grep. À titre d'exemple, j'ai créé les fichiers et répertoires suivants:

$ tree -a
.
├── a
├── .aa
├── .aa.bak
├── a.bck
├── b
├── .dir1
│   └── bb1.bak
├── dir2
│   └── bb2.bak
├── b.bak
├── c
├── c~
├── Documents
│   └── Documents.bak
├── exclude.txt
├── foo.backup
└── Music
    └── Music.bak

Si je comprends bien l'exemple que vous avez publié correctement, vous voulez déplacer a.bck, .aa.bak, b.bak, c~, foo.backupet dir2/bb2.bakà la poubelle et le congé .aa.bak, .dir1/bb1.bak, Documents/Documents.baket Music/Music.bakoù ils sont. J'ai donc créé le fichier exclude.txtavec le contenu suivant (vous pouvez en ajouter autant que vous le souhaitez):

$ cat exclude.txt 
./.*/
./Music
./Documents

J'utilise ./.*/parce que j'ai compris que votre découverte d'origine signifiait que vous vouliez déplacer les fichiers de sauvegarde cachés ( .foo) qui se trouvent dans le répertoire courant mais exclure tous les fichiers de sauvegarde qui se trouvent dans des répertoires cachés ( .foo/bar). Donc, je peux maintenant exécuter la findcommande et utiliser greppour exclure les fichiers indésirables:

$ find . -type f | grep -vZf exclude.txt | xargs -0 --no-run-if-empty trash-put

Options de grep:

   -v, --invert-match
          Invert  the  sense  of matching, to select non-matching
          lines.  (-v is specified by POSIX.)
   -f FILE, --file=FILE
          Obtain patterns from FILE, one  per  line.   The  empty
          file  contains  zero  patterns,  and  therefore matches
          nothing.  (-f is specified by POSIX.)
   -Z, --null
          Output a zero byte (the ASCII NUL character) instead of
          the  character  that normally follows a file name.  For
          example, grep -lZ outputs a zero byte after  each  file
          name  instead  of the usual newline.  This option makes
          the output unambiguous, even in the  presence  of  file
          names  containing  unusual  characters  like  newlines.
          This  option  can  be  used  with  commands  like  find
          -print0,  perl  -0,  sort  -z,  and xargs -0 to process
          arbitrary file names, even those that  contain  newline
          characters.
terdon
la source
Je suis vraiment désolé de ne pas être explicite. Veuillez voir la question révisée qui, je l'espère, est plus claire.
chandra
@chandra voir la réponse mise à jour, même idée générale, détails différents.
terdon
Je vous remercie. Vous avez répondu à ma question très clairement et parfaitement pour mon objectif. J'ai accepté ta réponse.
chandra
6

Avec GNU find (c'est-à-dire sous Linux non intégré ou Cygwin), vous pouvez utiliser -regexpour combiner tous ces -pathcaractères génériques en une seule expression régulière.

find . -regextype posix-extended \
     -type d -regex '\./(\..*|Music|Documents)' -prune -o \
     -type f -regex '.*(\.(bck|bak|backup)|~)' -print0 |
xargs -0 --no-run-if-empty trash-put

Avec FreeBSD ou OSX, utilisez -Eplutôt que -regextype posix-extended.

Gilles 'SO- arrête d'être méchant'
la source
Merci pour une excellente réponse alternative. Dommage que je ne puisse accepter deux réponses.
chandra
2

Regroupez-les -path ... -pruneen une seule expression en \( ... \)utilisant la logique -o( ou ).

find /somepath \( -path /a -prune -o \
                  -path /b -prune -o \
                  -path /c -prune \
               \) \
               -o -print

L'exemple ne sera pas des répertoires ou des fichiers Iterate à ou sous /somepath/a, /somepath/bet /somepath/c.

Voici un exemple plus spécifique utilisant plusieurs actions.

find / \( -path /dev -prune -o \
          -path /proc -prune -o \
          -path /sys -prune \
       \) \
       -o -printf '%p ' -exec cksum {} \;
JamesThomasMoon1979
la source
1

Cela semble être plus une question coquille qu'une findquestion. Avec un fichier contenant ( -name dir1 -o -name dir2 ) -prune(pas de "\"!), Vous pouvez simplement faire ceci:

find ... $(< /path/to/file)

Sans changer l'appel de recherche lui-même (en eval findou en changeant $ IFS), cela ne fonctionne qu'avec des chemins sans espace.

Si vous voulez garder le fichier plus simple, vous pouvez écrire un script.

# file content
dir1
dir2
dir3

# script content
#!/bin/bash
file=/path/to/file
# file may be checked for whitespace here
grep '[^[:space:]]' "$file" | { empty=yes
  while read dir; do
    if [ yes = "$empty" ]; then
      echo -n "( "
      empty=no
    else
      echo -n " -o "
    fi
    echo -n "-name ${dir}"
  done
  if [ no = "$empty" ]; then
    echo -n " ) -prune"
  fi; }

Et utilise

find ... $(/path/to/script)

au lieu.

Hauke ​​Laging
la source
Je suis vraiment désolé de ne pas être explicite. Veuillez voir la question révisée qui, je l'espère, est plus claire.
chandra
@chandra Je ne vois pas comment votre question est plus claire et je ne comprends pas ce qui pourrait être un problème avec ma solution (à l'exception de la réécriture triviale de -nameby path).
Hauke ​​Laging
Mon script ci-dessus fonctionne et fait ce que je veux. Je voulais simplement savoir s'il y avait une façon plus nette que\( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prune d'exclure certains répertoires de la recherche récursive qui le findfait. Je ne cherche rien dans les fichiers, mais plutôt la suppression de certains fichiers et l'évitement de certains répertoires dans mon chemin de recherche. Je ne comprends pas non plus ce que votre script essaie de faire. Il semble donc que nous ayons une mauvaise communication. Désolé. Laissez-nous en rester là.
chandra