Lista de argumentos muito longa para ls

48

Eu recebo o seguinte erro ao tentar ls *.txt | wc -lum diretório que contém muitos arquivos:

-bash: /bin/ls: Argument list too long

O limite dessa "lista de argumentos" depende da distribuição ou das especificações do computador? Normalmente, eu canalizaria o resultado de um resultado tão grande para outros comandos ( wc -lpor exemplo), então não estou preocupado com os limites do terminal.

zahypeti
fonte
6
Isso conta como análise lsde saída , que é uma má idéia, então é melhor evitá-lo. Para contar, consulte Qual é a melhor maneira de contar o número de arquivos em um diretório? , para uma solução alternativa complicada, veja por que o loop for não gera o erro "argumento muito longo"? .
manatwork
@manatwork Sim, eu também vi essas perguntas. Apenas imaginando uma maneira melhor de usar ou redirecionar uma saída longa de um comando de uma maneira mais geral.
você pode usar o getconf ARG_MAX para obter o limite na maioria dos sistemas baseados em unix
Prasanth

Respostas:

49

A lista de argumentos da mensagem de erro muito longa vem do * de ls *.txt.

Este limite é uma segurança para os programas binários e para o seu Kernel. Você verá nesta página mais informações sobre ele e como ele é usado e calculado.

Não existe esse limite no tamanho do tubo. Então você pode simplesmente emitir este comando:

find -type f -name '*.txt'  | wc -l

Nota: No Linux moderno, caracteres estranhos em nomes de arquivos (como novas linhas) serão escapados com ferramentas como lsou find, mas ainda serão exibidos em * . Se você estiver em um Unix antigo, precisará deste comando

find -type f -name '*.txt' -exec echo \;  | wc -l

NB2: Eu queria saber como é possível criar um arquivo com uma nova linha em seu nome. Não é tão difícil assim que você conhece o truque:

touch "hello
world"
Coren
fonte
1
Eu o modifiquei um pouco para funcionar no caso em que existem nomes de arquivos com novas linhas. Você também pode adicionar um -maxdepth 1se não pretender contar arquivos em subdiretórios.
Shawn J. Goff
Você não precisa do -exec echo \;.
Mikel
@ ShawnJ.Goff Eu testei. Não há necessidade de `eco 'na versão atual do GNU find
Coren
@Coren @Mikel - nem todo mundo tem GNU find. No findOS X e em sistemas baseados em busybox, e eu acho que qualquer sistema baseado em BSD mostra o nome do arquivo com uma nova linha, o que atrapalha a contagem.
Shawn J. Goff
Hã? wc -lestá contando novas linhas. Então, queremos que ele tenha novas linhas.
Mikel
11

Depende principalmente da sua versão do kernel do Linux.

Você poderá ver o limite do seu sistema executando

getconf ARG_MAX

que informa o número máximo de bytes que uma linha de comando pode ter após ser expandida pelo shell.

No Linux <2.6.23, o limite geralmente é de 128 KB.

No Linux> = 2.6.25, o limite é de 128 KB ou 1/4 do tamanho da pilha (consulte ulimit -s), o que for maior.

Veja a página de manual execve (2) para todos os detalhes.


Infelizmente, a tubulação ls *.txtnão vai resolver o problema, porque o limite está no sistema operacional, não no shell.

O shell expande o *.txte tenta chamar

exec("ls", "a.txt", "b.txt", ...)

e você tem tantos arquivos correspondentes *.txtque está excedendo o limite de 128 KB.

Você terá que fazer algo como

find . -maxdepth 1 -name "*.txt" | wc -l

em vez de.

(E veja os comentários de Shawn J. Goff abaixo sobre nomes de arquivos que contêm novas linhas.)

Mikel
fonte
Desculpe por não conseguir votar novamente. Precisa de mais reputação. :( Obrigado a todos !! #
Você poderia explicar o que significa .e o que -maxdepth 1significa na última linha? Obrigado! : D
Guilherme Salomé
2
@ GuilhermeSalomé .significa diretório atual, -maxdepth 1significa que não aparece em subdiretórios. O objetivo era combinar os mesmos arquivos que *.txt.
Mikel
9

Outra solução alternativa:

ls | grep -c '\.txt$'

Mesmo que lsproduza mais saída do que ls *.txtproduz (ou tente produzir), ele não se depara com o problema "argumento muito longo", porque você não está passando nenhum argumento para ls. Observe que grepusa uma expressão regular em vez de um padrão de correspondência de arquivo.

Você pode querer usar:

ls -U | grep -c '\.txt$'

(supondo que sua versão do lssuporte esta opção). Isso diz lspara não classificar sua saída, o que poderia economizar tempo e memória - e, nesse caso, a ordem não importa, já que você está apenas contando arquivos. Os recursos gastos na classificação da saída geralmente não são significativos, mas nesse caso já sabemos que você tem um número muito grande de *.txtarquivos.

E você deve reorganizar seus arquivos para não ter tantos em um único diretório. Isso pode ou não ser viável.

Keith Thompson
fonte
1

MAX_ARG_PAGES parece ser um parâmetro do kernel. Usar finde xargsé uma combinação típica para abordar esse limite, mas não tenho certeza de que funcione wc.

Canalizar a saída de find . -name \*\.txtum arquivo e contar as linhas nesse arquivo deve servir como uma solução alternativa.

Bram
fonte
Você pode fazer qualquer coisa com lsa saída de, não vai resolver isso. Desde que o curinga * .txt seja expandido acima do limite, falhará antes mesmo de iniciar lse gerar qualquer saída.
manatwork
É verdade que atualizei minha resposta.
Bram
Melhor. Mas, para substituí- lslo, você deve especificar -maxdepth 1para evitar a varredura recursiva dos subdiretórios.
Manatwork
Desculpe por não conseguir votar novamente. Precisa de mais reputação. :(
0

Isso pode estar sujo, mas funciona para as minhas necessidades e dentro da minha competência. Eu não acho que ele tenha um desempenho muito rápido, mas me permitiu continuar meu dia.

ls | grep jpg | <something>

Eu estava recebendo uma lista longa de 90.000 jpgs e os canalizava para avconv para gerar um intervalo de tempo.

Eu estava usando ls * .jpg | avconv antes de me deparar com esta questão.

Richard Bettridge
fonte