-bash: /usr/bin/mv: Lista de argumentos muito longa

-bash: /usr/bin/mv: Lista de argumentos muito longa

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 printfestá embutido bash, não há execve()nesse ponto. E cabe xargsdividir a lista de argumentos para evitar esse limite. Aqui usando a -topção específica do GNU do GNU mv.

Com zsh, você pode carregar o mvbuilt-in:

zmodload zsh/files
mv -- * ..

Ou use seu zargsajudante 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 oNqualificador glob para pular a classificação dos nomes dos arquivos ou o Nqualificador 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 stacksizelimite 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 mvcada nome individualmente.)

Ou ajude findvocê:

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 mvpossível.

Sem GNU mv, mas com um findque ainda conhece o não-padrão -mindepthe -maxdepthos 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.

informação relacionada