Quando estou usando este comando mv * ..
, estou recebendo o erro -bash: /usr/bin/mv: Argument list too long
. Eu entendo que isso ocorre porque o bash realmente expande o asterisco para cada arquivo correspondente, produzindo uma linha de comando muito longa. Como posso corrigir esse erro?
Responder1
O limite não está no bash, mas na execve()
chamada do sistema usada para executar comandos externos. Você poderia fazer:
printf '%s\0' * | xargs -r0 mv -t .. --
Como printf
está embutido bash
, não há execve()
nesse ponto. E cabe xargs
dividir a lista de argumentos para evitar esse limite. Aqui usando a -t
opção específica do GNU do GNU mv
.
Com zsh
, você pode carregar o mv
built-in:
zmodload zsh/files
mv -- * ..
Ou use seu zargs
ajudante para fazer a divisão:
autoload -Uz zargs # best in ~/.zshrc
zargs -r -- ./* -- mv -t ..
Você pode substituir ./*
por ./*(D)
para também mover arquivos ocultos ou adicionar o oN
qualificador glob para pular a classificação dos nomes dos arquivos ou o N
qualificador glob (com zargs -r
) para evitar o erro se não houver arquivo correspondente.
zargs -r -- ./*(ND) -- mv -t ..
Igual a:
print -rNC1 ./*(ND) | xargs -r0 mv -t ..
Mas sem a dependência do GNU xargs
.
No Linux (o kernel normalmente usado no Ubuntu), você também pode aumentar esse execve()
limite aumentando o stacksize
limite de recursos:
bash-5.1$ /bin/true {1..200000}
bash: /bin/true: Argument list too long
bash-5.1$ ulimit -s unlimited
bash-5.1$ /bin/true {1..200000}
bash-5.1$
Não é totalmente ilimitado (pelo menos não nas versões atuais do kernel):
bash-5.1$ /bin/true {1..2000000}
bash: /bin/true: Argument list too long
Observe que o limite está no tamanho acumulado dos argumentos e nas variáveis de ambiente passadas para execve()
, mas o cálculo não é apenas a soma dos bytes e a forma como isso é feito varia entre o sistema operacional e a versão do mesmo.
Responder2
Você poderia fazer isso em duas ou mais etapas:
mv [a-k]* .. # or some other pattern matching a subset of the files
mv -- * ..
Ou em um loop,
for name in *; do
mv -- "$name" ..
done
(Mas isso exigiria mv
cada nome individualmente.)
Ou ajude find
você:
find . -mindepth 1 -maxdepth 1 -exec mv -t .. -- {} +
Isso encontraria todos os nomes no diretório atual e, usando GNU mv
, os moveria para o diretório acima com o mínimo de invocações mv
possível.
Sem GNU mv
, mas com um find
que ainda conhece o não-padrão -mindepth
e -maxdepth
os predicados,
find . -mindepth 1 -maxdepth 1 -exec sh -c 'mv -- "$@" ..' sh {} +
Nenhuma dessas variações se preocupa com colisões de nomes. Você deve testar os dados com backup adequado.