Passando vários diretórios para a opção -prune no find

8

Estou usando find para localizar e excluir arquivos de backup, mas desejo excluir determinados diretórios da pesquisa. Os nomes dos arquivos de backup podem terminar em .bck , bak , ~ ou backup .

O código de exemplo mínimo de trabalho (MWE) com apenas três diretórios a serem excluídos é:

#! /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

A sintaxe \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -prune parece um pouco desajeitada, especialmente se houver cerca de dez diretórios a serem excluídos, embora eu tenha mostrado apenas três no MWE.

Existe uma maneira mais elegante de usar um arquivo de entrada, com a lista de diretórios excluídos, ou uma construção semelhante a uma matriz ou lista, que poderia ser pressionada em serviço?

Sinto muito por não ser mais explícito quando escrevi minha pergunta original.

NB: trash-put é um utilitário que move os arquivos para o Trashcan em vez de excluí-los [1].

[1] link

    
por chandra 28.04.2013 / 11:26

4 respostas

4

Até onde sei, não há opção para informar find para ler padrões de um arquivo. Uma solução fácil é salvar os padrões que desejo excluir em um arquivo e passar esse arquivo como entrada para um reverso grep . Como exemplo, criei os seguintes arquivos e diretórios:

$ 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

Se eu entendi o exemplo que você postou corretamente, você deseja mover a.bck , .aa.bak , b.bak , c~ , foo.backup e dir2/bb2.bak para a lixeira e deixar .aa.bak , .dir1/bb1.bak , Documents/Documents.bak e Music/Music.bak onde estão. Portanto, criei o arquivo exclude.txt com o seguinte conteúdo (você pode adicionar quantos desejar):

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

Eu uso ./.*/ porque entendi sua descoberta original como significando que você deseja mover arquivos de backup ocultos ( .foo ) que estão no diretório atual mas excluem todos os arquivos de backup que estão em diretórios ocultos ( .foo/bar ) . Portanto, agora posso executar o comando find e usar grep para excluir arquivos indesejados:

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

Opções do 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.
    
por 28.04.2013 / 15:48
6

Com o GNU find (por exemplo, em Linux ou Cygwin não integrado), você pode usar -regex para combinar todos esses -path wildcards em uma única regex.

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

Com o FreeBSD ou o OSX, use -E em vez de -regextype posix-extended .

    
por 30.04.2013 / 03:05
1

Esta parece ser mais uma questão de shell do que uma questão find . Com um arquivo contendo ( -name dir1 -o -name dir2 ) -prune (no "\"!) Você pode simplesmente fazer isso:

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

Sem alterar a própria chamada de localização (para eval find ou alterando $ IFS), isso funciona apenas com caminhos sem espaço em branco.

Se você quiser manter o arquivo mais simples, pode escrever um 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; }

E use

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

em vez disso.

    
por 28.04.2013 / 12:40
1

Agrupe -path ... -prune em uma expressão delimitada por \( ... \) usando a lógica -o ( ou ).

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

O exemplo não irá repetir diretórios ou arquivos em /somepath/a , /somepath/b e /somepath/c .

Aqui está um exemplo mais específico usando várias ações.

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