Pasar múltiples directorios a la opción -prune en find


9

Estoy utilizando findpara localizar y eliminar archivos de copia de seguridad, pero deseo excluir ciertos directorios de la búsqueda. Los nombres de los archivos de copia de seguridad podrían terminar en .bck, bak, ~, o backup.

El código de Ejemplo de trabajo mínimo (MWE) con solo tres directorios para excluir es:

#! /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 sintaxis \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneparece un poco torpe, especialmente si hay alrededor de diez directorios para excluir, aunque solo he mostrado tres en el MWE.

¿Hay alguna forma más elegante de usar un archivo de entrada, con la lista de directorios excluidos, o una construcción tipo matriz o de lista, que podría ser puesta en servicio?

Lamento no haber sido más explícito cuando escribí mi pregunta original.

NB: trash-putes una utilidad que mueve los archivos al en Trashcanlugar de eliminarlos [1].

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

Respuestas:


4

Hasta donde yo sé, no hay opción para decir findque lea los patrones de un archivo. Una solución fácil es guardar los patrones que quiero excluir en un archivo y pasar ese archivo como entrada para un reverso grep. Como ejemplo, he creado los siguientes archivos y directorios:

$ 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 he entendido el ejemplo informados correctamente, que desea mover a.bck, .aa.bak, b.bak, c~, foo.backupy dir2/bb2.baka la basura y la licencia .aa.bak, .dir1/bb1.bak, Documents/Documents.baky Music/Music.bakdonde están. Por lo tanto, he creado el archivo exclude.txtcon los siguientes contenidos (puede agregar tantos como desee):

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

Utilizo ./.*/porque entendí que su hallazgo original significa que desea mover archivos de copia de seguridad ocultos ( .foo) que están en el directorio actual pero excluye cualquier archivo de copia de seguridad que esté en directorios ocultos ( .foo/bar). Entonces, ahora puedo ejecutar el findcomando y usarlo greppara excluir archivos no deseados:

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

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

Lamento mucho no haber sido explícito. Por favor, vea la pregunta revisada que espero sea más clara.
chandra

@chandra ver respuesta actualizada, misma idea general, detalles diferentes.
terdon

Gracias. Has respondido mi pregunta muy clara y perfectamente para mi propósito. He aceptado tu respuesta
chandra

6

Con GNU find (es decir, bajo Linux no integrado o Cygwin), puede usar -regexpara combinar todos estos -pathcomodines en una sola expresión regular.

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

Con FreeBSD u OSX, use en -Elugar de -regextype posix-extended.


Gracias por una excelente respuesta alternativa. Es una pena que no pueda aceptar dos respuestas.
chandra

2

Agrupe -path ... -pruneen una expresión encerrada \( ... \)usando -o( o ) lógica.

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

El ejemplo directorios o archivos en o bajo no iterar /somepath/a, /somepath/by /somepath/c.

Aquí hay un ejemplo más específico que usa múltiples acciones.

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

1

Esto parece ser más una pregunta de concha que una findpregunta. Con un archivo que contiene ( -name dir1 -o -name dir2 ) -prune(¡no "\"!) Simplemente puede hacer esto:

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

Sin eval findembargo, sin cambiar la llamada de búsqueda en sí ( ao cambiando $ IFS), esto funciona solo con rutas sin espacios en blanco.

Si desea mantener el archivo más simple, puede escribir 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; }

Y use

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

en lugar.


Lamento mucho no haber sido explícito. Por favor, vea la pregunta revisada que espero sea más clara.
chandra

@chandra No veo cómo su pregunta es más clara ni entiendo qué podría ser un problema con mi solución (excepto por la reposición trivial de -nameby path).
Hauke ​​Laging

Mi script anterior funciona y hace lo que quiero que haga. Simplemente quería saber si había una forma más ordenada que \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneexcluir ciertos directorios de la búsqueda recursiva que lo findhace. No busco nada dentro de los archivos, sino que elimino ciertos archivos y evito ciertos directorios en mi ruta de búsqueda. Tampoco entiendo lo que tu script intenta hacer. Entonces, parece que tenemos una falta de comunicación. Lo siento. Dejémoslo así.
chandra
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.