Eu tenho uma pasta com mais de um milhão de arquivos que precisa ser classificada, mas não posso fazer nada porque mv
gera essa mensagem o tempo todo
-bash: /bin/mv: Argument list too long
Estou usando este comando para mover arquivos sem extensão:
mv -- !(*.jpg|*.png|*.bmp) targetdir/
Respostas:
xargs
é a ferramenta para o trabalho. Isso oufind
com-exec … {} +
. Essas ferramentas executam um comando várias vezes, com tantos argumentos quanto podem ser passados de uma só vez.Ambos os métodos são mais fáceis de executar quando a lista de argumentos variáveis está no final, o que não é o caso aqui: o argumento final para
mv
é o destino. Com os utilitários GNU (ou seja, no Linux ou Cygwin não incorporado), a-t
opção tomv
é útil para passar o destino primeiro.Se os nomes dos arquivos não tiverem espaço em branco nem nenhum
\"'
, você poderá simplesmente fornecer os nomes dos arquivos como entradaxargs
(oecho
comando é um bash integrado, portanto, não está sujeito ao limite de comprimento da linha de comando):Você pode usar a
-0
opção paraxargs
usar entrada delimitada por nulo em vez do formato entre aspas padrão.Como alternativa, você pode gerar a lista de nomes de arquivos com
find
. Para evitar a recursão em subdiretórios, use-type d -prune
. Como nenhuma ação é especificada para os arquivos de imagem listados, apenas os outros arquivos são movidos.(Isso inclui arquivos de ponto, ao contrário dos métodos curinga do shell.)
Se você não possui utilitários GNU, pode usar um shell intermediário para obter os argumentos na ordem correta. Este método funciona em todos os sistemas POSIX.
No zsh, você pode carregar o
mv
builtin :ou se você preferir permitir
mv
e outros nomes continuem se referindo aos comandos externos:ou com globs no estilo ksh:
Como alternativa, usando GNU
mv
ezargs
:fonte
shopt -s extglob
habilitá-lo. Eu tinha perdido um passo nosfind
comandos, eu os consertei.find
comandos que eu publiquei (agora) funcionam. Você deve ter deixado uma peça ao copiar e colar.!
? É mais explícito e mais fácil de entender do que a trilha ímpar-o
. Por exemplo,! -name '*.jpg' -a ! -name '*.png' -a ! -name '*.bmp'
Se trabalhar com o kernel Linux é suficiente, você pode simplesmente fazer
isso funcionará porque o kernel do Linux incluiu um patch há cerca de 10 anos que mudou o limite de argumentos para se basear no tamanho da pilha: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/ confirmar /? id = b6a2fea39318e43fee84fa7b0b90d68bed92d2ba
Atualização: Se você se sente corajoso, pode dizer
e você ficará bem com todas as expansões de shell, desde que tenha RAM suficiente.
fonte
ulimit -s unlimited
-lo e ele funcionará para arquivos praticamente ilimitados.ulimit -s unlimited
o limite da linha de comando real, é 2 ^ 31 ou 2 GB. (MAX_ARG_STRLEN
Fonte no kernel.)O limite de aprovação de argumentos do sistema operacional não se aplica a expansões que ocorrem dentro do interpretador de shell. Portanto, além de usar
xargs
orfind
, podemos simplesmente usar um loop de shell para dividir o processamento emmv
comandos individuais :Isso usa apenas recursos e utilitários da POSIX Shell Command Language. Essa linha única é mais clara com recuo, com ponto e vírgula desnecessário removido:
fonte
mv
processos, em vez de apenas os poucos necessários usando afind
solução POSIX @Gilles postada. Em outras palavras, dessa maneira resulta em muitas rotações desnecessárias da CPU.case
declaração sobre o resultado da*
expansão para filtrar várias extensões é equivalente à!(*.jpg|*.png|*.bmp)
expressão original . Afind
resposta não é de fato equivalente; desce em subdiretórios (não vejo um-maxdepth
predicado).-name . -o -type d -prune -o
protege de descer para subdiretórios.-maxdepth
aparentemente não é compatível com POSIX, embora isso não seja mencionado na minhafind
página de manual.Para uma solução mais agressiva do que as oferecidas anteriormente, acesse a fonte do kernel e edite
include/linux/binfmts.h
Aumente o tamanho
MAX_ARG_PAGES
para algo maior que 32. Isso aumenta a quantidade de memória que o kernel permitirá para argumentos do programa, permitindo que você especifique seu comandomv
ourm
para um milhão de arquivos ou o que estiver fazendo. Recompile, instale, reinicie.Cuidado! Se você definir isso muito grande para a memória do sistema e executar um comando com muitos argumentos, COISAS MAU ACONTECEREM! Seja extremamente cauteloso ao fazer isso em sistemas multiusuário, pois torna mais fácil para usuários mal-intencionados usar toda a sua memória!
Se você não sabe como recompilar e reinstalar seu kernel manualmente, provavelmente é melhor que você apenas finja que essa resposta não existe por enquanto.
fonte
Uma solução mais simples usando em
"$origin"/!(*.jpg|*.png|*.bmp)
vez de um bloco catch:Obrigado a @Score_Under
Para um script de várias linhas, você pode fazer o seguinte (observe o
;
antes dodone
lançamento):Para fazer uma solução mais generalizada que mova todos os arquivos, você pode fazer o seguinte procedimento:
Que fica assim se você fizer o recuo:
Isso pega todos os arquivos na origem e os move um a um para o destino. As aspas
$file
são necessárias caso haja espaços ou outros caracteres especiais nos nomes dos arquivos.Aqui está um exemplo desse método que funcionou perfeitamente
fonte
!(*.jpg|*.png|*.bmp)
. Você pode adicionar isso ao seu loop for observando o"$origin"/!(*.jpg|*.png|*.bmp)
que evitaria a necessidade da chave usada na resposta de Kaz e manteria o corpo simples do loop for.Às vezes é mais fácil escrever um pequeno script, por exemplo, em Python:
fonte
Você pode contornar essa restrição enquanto ainda estiver usando,
mv
se não se importar de executá-la algumas vezes.Você pode mover partes de cada vez. Digamos, por exemplo, que você tenha uma longa lista de nomes de arquivos alfanuméricos.
Isso funciona. Então nocauteie outro pedaço grande. Depois de alguns movimentos, você pode voltar a usar
mv ./subdir/* ./
fonte
Aqui estão meus dois centavos, acrescente isso
.bash_profile
Uso
fonte