Seu comando não funcionará como esperado por vários motivos:
-
bash avalia
'dirname {}'
antes o comando find é executado.Isso pode ser resolvido chamando o bash para avaliar a saída do comando
dirname
-
O
-execdir
muda para o diretório do arquivo encontrado antes de executar o comando.Portanto, mesmo com a sintaxe adequada,
dirname {}
será sempre.
. -
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 omv
para cada arquivo emdata/1
, mas funciona bem de outra forma. Quando termina o processamento dedata/1
, a localização fica "perdida" de alguma forma. -
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 dedirname
precisa ser citada.Usar
mv "'dirname "{}"'"
(oumv "$("{}")"
, que é mais legível) funcionará bem com espaços, mas pode levar a conseqüências desastrosas em geral 1 . -
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.