Palavras-chave: shell, Argument list too long, /bin/rm, /bin/mv, bash, ksh, sh
Sistemas operacionais UNIX normalmente têm um tamanho fixo de memória que pode ser utilizada para variáveis de ambiente e argumentos na chamada de um programa. Este tamanho varia de sistema operacional para sistema operacional. Para obter este valor pode-se executar o seguinte comando:
$ getconf ARG_MAX 131072
Quando executamos algum comando e passamos (incluindo o nome, o caminho do programa e variáveis de ambiente) uma quantidade de argumentos que supere o definido em ARG_MAX o programa é terminado com a seguinte mensagem:
shell: comando: Argument list too long
Vamos simular este comportamento criando uma grande quantidade de arquivos em um diretório. O número de arquivos que precisam ser criados pode variar de acordo com o tamanho definido em ARG_MAX.
$ for i in $(seq 1 50000); do touch $i; done $ rm * bash: /bin/rm: Argument list too long $ ls * bash: /bin/ls: Argument list too long $ mv * /tmp/ /bin/bash: /bin/mv: Argument list too long $ grep a * bash: /bin/grep: Argument list too long $ chmod 555 * bash: /bin/chmod: Argument list too long
Existem várias maneiras de contornar este “problema”. Serão listadas algumas:
$ find . -name '*' -print0 | xargs -0 rm $ find . -type f -print0 | xargs -0 rm $ find . -type f -exec rm {} \\; $ echo * | xargs rm
Dica: usando o xargs é mais rápido!
Um leitor mais atendo notaria que a última solução apresentada não poderia funcionar, já que o * utilizado seria expandido para todos os arquivos e excederia o tamanho definido em ARG_MAX.
O echo é um comando builtin do shell, ou seja, um novo programa não é executado (chamada de sistema exec não é invocada). Se utilizarmos o comando echo do sistema operacional o erro será mostrado:
$ /bin/echo * bash: /bin/echo: Argument list too long $ echo * lista de todos os arquivos do diretório
A solução com echo não funciona se existir espaços em branco no nome dos arquivos.
Seria legal explicar que as versões com o xargs são mais rápidas que a versão com o “-exec” do find porque a do -exec vai executar um “rm” pra remover cada arquivo enquanto as que usam xargs vão executar um “rm” pra cada lista de arquivos que caibam sem estourar uma linha de comando.
A primeira versão (find . -name ‘*’ -print0 | xargs -0 rm) vai passar o diretório corrente “.” e qualquer outro subdiretório do diretório corrente para o rm, o que vai gerar algumas mensagens de erro inócuas. A segunda versão me parece mais interessante.
O “truque” do echo builtin é bem interessante, mas vale a pena ressaltar que nem todas as shells possuem esta builtin. Algumas delas usam o comando /bin/echo. Portanto, acho melhor evitar este tipo de comando num script. Há, também, uma diferença em relação às versões com o find. O “echo *” não ecoa os arquivos com nomes que comecem com ponto. Já o find sim. Isso pode gerar dúvidas.
Na minha opinião, a melhor alternativa, por ser a mais geral e mais eficiente, é a segunda: find . -type f -print0 | xargs -0 rm.
Gustavo.
Gustavo,
Simplesmente perfeito! Executei o comando e excluiu tudo do diretório.
Obrigada pela dica!