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

9

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

O código do 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" \) -pruneparece 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 possa ser pressionada para o serviço?

Lamento não ter sido mais explícito quando escrevi minha pergunta original.

Nota: trash-puté um utilitário que move os arquivos para, em Trashcanvez de excluí-los [1].

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

Chandra
fonte

Respostas:

4

Tanto quanto eu sei, não há opção para dizer findpara 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 uma reversão grep. Como exemplo, eu 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.backupe dir2/bb2.bakpara o lixo e licença .aa.bak, .dir1/bb1.bak, Documents/Documents.bake Music/Music.bakonde eles estão. Portanto, criei o arquivo exclude.txtcom o seguinte conteúdo (você pode adicionar quantos quiser):

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

Eu uso ./.*/porque entendi sua descoberta original significa que você deseja mover arquivos de backup ocultos ( .foo) que estão no diretório atual, mas exclui quaisquer arquivos de backup que estão em diretórios ocultos ( .foo/bar). Portanto, agora posso executar o findcomando e usá-lo greppara excluir arquivos indesejados:

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

Opções 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
fonte
Sinto muito por não ser explícito. Por favor, veja a pergunta revisada que, espero, seja mais clara.
Página Inicial
@chandra veja resposta atualizada, mesma ideia geral, detalhes diferentes.
terdon
Obrigado. Você respondeu à minha pergunta de maneira muito clara e perfeita para o meu propósito. Eu aceitei sua resposta.
usar o seguinte
6

Com o GNU find (ou seja, no Linux ou Cygwin não incorporado), você pode usar -regexpara combinar todos esses -pathcuringas em um único 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 OSX, use em -Evez de -regextype posix-extended.

Gilles 'SO- parar de ser mau'
fonte
Obrigado por uma excelente resposta alternativa. É uma pena que eu não possa aceitar duas respostas.
usar o seguinte
2

Agrupe -path ... -pruneem uma expressão fechada \( ... \)usando -o( ou ) lógica.

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

O exemplo vai diretórios não ITERATE ou arquivos ou sob /somepath/a, /somepath/be /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 {} \;
JamesThomasMoon1979
fonte
1

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

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

Sem alterar a chamada de busca em si (para eval findou alterando $ IFS), isso funciona com caminhos sem apenas espaço em branco.

Se você deseja 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 de.

Hauke ​​Laging
fonte
Sinto muito por não ser explícito. Por favor, veja a pergunta revisada que, espero, seja mais clara.
Página Inicial
@chandra Não vejo como sua pergunta é mais clara nem entendo o que poderia ser um problema com minha solução (exceto a substituição trivial de -namepor path).
Hauke Laging
Meu script acima funciona e faz o que eu quero. Eu simplesmente queria saber se havia uma maneira mais clara do \( -path "dir1" -o -path "dir2" ... -o -path "dirN" \) -pruneque excluir determinados diretórios da pesquisa recursiva que existe find. Não estou procurando nada nos arquivos, mas excluindo determinados arquivos e evitando determinados diretórios no meu caminho de pesquisa. Também não entendo o que seu script está tentando fazer. Então, parece que temos uma falta de comunicação. Desculpa. Vamos deixar assim.
precisa