Se eu quiser que o comando find pare depois de encontrar um certo número de correspondências, como faço isso?
O pano de fundo é que eu tenho muitos arquivos em uma pasta, preciso colocá-los em pastas separadas aleatoriamente como:
find -max-matches 1000 -exec mv {} /path/to/collection1 \+;
find -max-matches 1000 -exec mv {} /path/to/collection2 \+;
isso é possível fazer find
sozinho? Se não, qual seria a maneira mais simples de fazer isso?
Você pode implementar novos testes para
find
usar-exec
:moverá os primeiros 1000 arquivos encontrados para
/path/to/collection1
.Isso funciona da seguinte forma:
seq 1 1000
produz 1000 linhas, canalizadas parafind
;-exec read
lê uma linha, falhando se o pipe estiver fechado (quandoseq
a saída de 's foi consumida);-exec
for bem sucedido,-exec mv ...
executa o movimento.-exec ... +
funciona como esperado:read
será executado uma vez por iteração, masfind
acumulará arquivos correspondentes e chamarámv
o menor número de vezes possível.Isso se baseia no fato de que
find
's é-exec
bem-sucedido ou falha com base no status de saída do comando executado: quandoread
bem-sucedido,find
continua processando as ações fornecidas acima (porque o operador padrão é “e”) e, quando falha,find
para.Se você
find
apoia a-quit
ação, você pode usar isso para melhorar a eficiência:Sem isso,
find
testará todos os arquivos, mesmo que mantenha apenas 1000 paramv
.Estou assumindo que
read
está disponível como um comando externo e implementa a especificação POSIX pararead
; se esse não for o caso,sh -c read
pode ser usado em seu lugar. Em ambos os casos,find
iniciará um processo separado para cada arquivo verificado.Como você não está usando
find
muito além de percorrer a árvore de diretórios, sugiro usar o shell diretamente para fazer isso. Veja variações para amboszsh
ebash
abaixo.Usando a
zsh
cascaO padrão globbing
./**/*(-.D[1,1000])
corresponderia a todos os arquivos regulares (ou links simbólicos para esses arquivos) dentro ou sob o diretório atual e, em seguida, retornaria o 1000 primeiro deles. O-.
restringe a correspondência a arquivos regulares ou links simbólicos para eles, enquantoD
age comodotglob
inbash
(corresponde a nomes ocultos).Isso está assumindo que o comando gerado não cresceria muito através da expansão do padrão de globbing ao chamar
mv
.O acima é bastante ineficiente, pois expandiria o glob para cada coleção. Você pode, portanto, armazenar os nomes de caminho em uma matriz e, em seguida, mover as fatias disso:
Para randomizar o
pathnames
array ao criá-lo (você mencionou querer mover arquivos aleatórios):Você pode fazer uma coisa semelhante em
bash
(exceto que não pode embaralhar facilmente o resultado de uma correspondência glob embash
, além de possivelmente alimentar os resultados por meioshuf
de , então vou pular essa parte):Eu não acho que isso pode ser feito com apenas
find
. Você pode usar algo como:-print0
,-z
e-0
trabalhem juntos para garantir que tudo funcione mesmo com feeds de linha em nomes de arquivos.Eu acho que não é possível diretamente com find, mas você pode usar um pipe com head e xargs como:
Isso move os primeiros 1.000 arquivos para a coleção1.
A resposta 264963 de Stephens é provavelmente a melhor para o meu caso de uso, mas há uma solução simples para o caso de uso nesta questão com apenas find e head:
O
-print
será avaliado antes do-exec
(pelo menos no CentOS 8) e o pipe to head fará comfind
que saia quandohead
fechar o pipe.