Limite de comprimento da linha de comandos: built-in vs executável

1

Assim, por especificação POSIX , temos a seguinte definição para * :

Expands to the positional parameters, starting from one, initially producing one field for each positional parameter that is set. When the expansion occurs in a context where field splitting will be performed, any empty fields may be discarded and each of the non-empty fields shall be further split as described in Field Splitting. When the expansion occurs in a context where field splitting will not be performed, the initial fields shall be joined to form a single field with the value of each parameter separated by the first character of the IFS variable if IFS contains at least one character, or separated by a if IFS is unset, or with no separation if IFS is set to a null string.

Para a grande maioria das pessoas, estamos cientes da famosa limitação ARG_MAX :

$ getconf ARG_MAX
2621440

que pode levar a:

$ cat * | sort -u > /tmp/bla.txt
-bash: /bin/cat: Argument list too long
Felizmente, as pessoas boas por trás de bash ([incluem todos os outros semelhantes a POSIX]) nos forneceram printf como um built-in, então podemos simplesmente:

printf '%s
$ cat *
' * | sort -u --files0-from=- > /tmp/bla.txt

E tudo é transparente para o usuário.

Alguém poderia, por favor, me avisar por que é tão trivial ignorar a limitação ARG_MAX usando o comando built-in e por que é tão difícil fornecer um interpretador de shell POSIX que manipule o parâmetro * special? para um executável autônomo:

$ getconf ARG_MAX
2621440

Isso quebraria alguma coisa? Eu não estou pedindo a bash pessoas para fornecer cat como um built-in, estou interessado apenas na ordem de operações e por que é * expandido em comportamento diferente, dependendo se o comando é embutido ou é um autônomo executável.

    
por malat 16.05.2017 / 10:38

2 respostas

9

A limitação não está no shell, mas na família de funções exec() .

O padrão POSIX diz em relação a este :

The number of bytes available for the new process' combined argument and environment lists is {ARG_MAX}. It is implementation-defined whether null terminators, pointers, and/or any alignment bytes are included in this total.

Para executar utilitários que são construídos no shell, o shell não precisará chamar exec() , por isso não é afetado por esta limitação.

Note, também, que não é simplesmente o tamanho da linha de comando que é limitada, mas a combinação do comprimento do comando, seus argumentos e as variáveis de ambiente atuais e seus valores.

Observe também que printf não é um utilitário incorporado em, por exemplo, pdksh (que funciona como sh e ksh no OpenBSD). Baseando-se em ser um built-in terá que levar em conta o shell específico que está sendo usado.

    
por 16.05.2017 / 10:47
7

Kusalananda 's answer explica por que ARG_MAX não é um problema com os built-ins do shell.

No que diz respeito à implementação de cat * de uma forma que não seja afetada por ARG_MAX , fazer isso é trivial: tudo o que a implementação de cat precisa fazer é usar glob(3) para implementar seu próprio globbing, e então você o executaria usando cat \* ou cat '*' para que o shell não o faça t fazer o seu próprio globbing. Você encontrará alguns comandos em um sistema no estilo Linux ou Unix que pode cuidar de seus próprios globbing, pelo menos em certas circunstâncias; find , tar , zip etc. Muitos comandos com versões DOS nativas pelo menos incluíam código para lidar com globbing, já que os shells não dominam os próprios argumentos dos comandos externos.

Dadas as expectativas do shell POSIX, esse recurso seria bastante surpreendente e difícil de descobrir! Nas primeiras versões do Unix, o globbing foi implementado usando um programa separado, /etc/glob .

    
por 16.05.2017 / 10:53