Removendo um diretório contendo um arquivo usando find

3

Estou pesquisando uma lista de diretórios para determinados arquivos e desejo excluir o diretório em que o arquivo está.

É isso que estou tentando:

find /converter/storage/unmatched/ -size -4b -name '*.mp3' -execdir mv -b 'dirname {}' /converter/storage/smallfiles/ \;
mv: cannot move '.' to '/converter/storage/smallfiles/.'

Estou tentando movê-lo primeiro para provar a mim mesmo que isso pode ser feito sem destruir arquivos indesejados. Como faço para mover ou excluir o diretório que contém?

    
por Josh Smeaton 07.03.2013 / 02:31

1 resposta

3

Seu comando não funcionará como esperado por vários motivos:

  1. bash avalia ​'dirname {}'​ antes o comando find é executado.

    Isso pode ser resolvido chamando o bash para avaliar a saída do comando dirname

  2. O -execdir muda para o diretório do arquivo encontrado antes de executar o comando.

    Portanto, mesmo com a sintaxe adequada, dirname {} será sempre . .

  3. Como o find está passando por todos os arquivos dentro de todos os diretórios (em vez dos próprios diretórios), mover o diretório que está sendo processado atualmente pode causar um comportamento inesperado.

    Eu não sei encontrar bem o suficiente para entrar em detalhes, mas o exemplo a seguir deve ilustrar isso.

    $ find data -type f -printf "  %p" ; echo
      data/1/b  data/1/a  data/2/b  data/2/a
    $
    $ find data -type f -exec bash -c ' \
    > echo -n "  "mv 'dirname {}' backup/
    > ' \; ; echo
      mv data/1 backup/  mv data/1 backup/  mv data/2 backup/  mv data/2 backup/
    $
    $ find data -type f -exec bash -c '
    > echo -n "  "mv 'dirname {}' backup/; mv 'dirname "{}"' backup/
    > ' \; ; echo
      mv data/1 backup/  mv data/1 backup/
    mv: cannot stat 'data/1': No such file or directory
    

    Depois de mover o diretório data/1 , o find continua processando seu conteúdo. Isso causa uma mensagem de erro, pois o find executa o mv para cada arquivo em data/1 , mas funciona bem de outra forma. Quando termina o processamento de data/1 , a localização fica "perdida" de alguma forma.

  4. Enquanto o find lida com todos os caracteres possíveis com significado especial corretamente se {} ocorrer como um argumento simples para ser encontrado, os comandos no exemplo 3 não serão executados adequadamente se, por exemplo, ocorrer um espaço em um caminho. Para evitar isso, a saída do argumento de dirname precisa ser citada.

    Usar mv "'dirname "{}"'" (ou mv "$("{}")" , que é mais legível) funcionará bem com espaços, mas pode levar a conseqüências desastrosas em geral 1 .

  5. mv -b não pode criar diretórios de backup, apenas arquivos.

Como você descobriu, o problema 3 só ocorre se você tentar mover o diretório, mas não se, por exemplo, removê-lo. Portanto, uma solução fácil e segura para o problema 3 (que corrige o problema 5 no processo) está movendo os diretórios correspondentes para um arquivo zip. 2

O problema 4 pode ser resolvido passando {} como argumento para bash e acessando-o como $0 .

O comando

find /converter/storage/unmatched/ -type f -size -4b -name '*.mp3' \ -exec bash -c ' \
  zip -0mqr smallfiles.zip "$(dirname "$0")"\
' {} \;

funcionará como esperado, embora ainda gere mensagens de erro ao tentar mover um diretório mais de uma vez.

Uma abordagem um pouco mais limpa (ou seja, sem mensagens de erro) seria usar find -depth -type d para passar pelos próprios diretórios. 3

Para cada diretório, podemos usar

find "$0" -maxdepth 1 -type f -size -4b -name '*.mp3' -printf 1 -quit

para imprimir 1 se e somente se ele for removido 4 , e teste ( [ ) para verificar se encontrou 1 .

O comando

find /converter/storage/unmatched/ -depth -type d -exec bash -c ' \
  [ $(find "$0" -maxdepth 1 -type f -size -4b -name '*.mp3' -printf 1 -quit) ] \
  && echo zip -0mqr smallfiles.zip "$0"\
  ' {} \;

terá o efeito desejado.

1 Se o nome de um diretório dentro de inigualável consistir, por exemplo, em um único caractere de nova linha, "$(dirname "{}")" será avaliado como unmatched e $(dirname {}) irá avaliar . (ou seja, a pasta da qual você está executando o comando)!

2 Mesmo sem compactar os arquivos, isso levará muito mais tempo do que apenas (re) movê-los se os arquivos forem grandes. Como sua pasta de destino é chamada smallfiles , suspeito que isso não seja um problema.

3 A opção -depth 'faz com que o processo de localização de cada diretório seja processado antes de processar o próprio diretório. Isso evita as mensagens de erro que recebemos com a primeira abordagem.

4 A opção -quit faz com que o find pare após encontrar a primeira correspondência. Não é estritamente necessário, mas pode acelerar um pouco as coisas.

    
por 07.03.2013 / 03:09

Tags