A partir desta pergunta sobre se printf é um built-in para yash , vem esta resposta que cita o padrão POSIX .
A resposta aponta que a sequência de pesquisa POSIX é encontrar uma implementação externa do comando desejado e, em seguida, se o shell o implementou como interno, execute o interno. (Para built-ins que não são built-ins especiais .)
Por que o POSIX tem esse requisito para que uma implementação externa exista antes de permitir que uma implementação interna seja executada?
Parece... arbitrário, então estou curioso.
Esta é uma regra "como se".
Simplificando: o comportamento do shell como os usuários veem não deve mudar se uma implementação decidir tornar um comando externo padrão também disponível como shell embutido.
O contraste que mostrei em https://unix.stackexchange.com/a/496291/5132 entre os comportamentos (por um lado) dos shells PD Korn, MirBSD Korn e Heirloom Bourne; (por outro lado) os shells Z, 93 Korn, Bourne Again e Debian Almquist; e (na mão que agarra) a concha de Watanabe destaca isso.
Para os shells que não possuem
printf
um built-in, removendo/usr/bin
dePATH
faz uma invocação deprintf
parar de funcionar. O comportamento conforme POSIX, exibido pelo shell Watanabe em seu modo conforme, causa o mesmo resultado. O comportamento do shell que possui umprintf
built-in é como se estivesse invocando um comando externo.Considerando que o comportamento de todos os shells não conformes não se altera se
/usr/bin
for removido dePATH
, e eles não se comportam como se estivessem invocando um comando externo.O que o padrão está tentando garantir a você é que os shells podem incorporar todos os tipos de comandos normalmente externos (ou implementá-los como suas próprias funções de shell), e você ainda obterá o mesmo comportamento dos built-ins que você fez com os comandos externos se você ajustar
PATH
para impedir que os comandos sejam encontrados.PATH
continua sendo sua ferramenta para selecionar e controlar quais comandos você pode invocar.(Como explicado em https://unix.stackexchange.com/a/448799/5132 , anos atrás, as pessoas escolhiam a personalidade de seu Unix alterando o que estava acontecendo
PATH
.)Pode-se opinar que fazer o comando sempre funcionar, independentemente de ele ser encontrado,
PATH
é, na verdade, o objetivo de criar comandos normalmente externos embutidos. (É por isso que meu conjunto de ferramentas nosh ganhou umprintenv
comando embutido na versão 1.38, na verdade. Embora isso não seja um shell.)Mas o padrão está lhe dando a garantia de que você verá o mesmo comportamento para comandos externos regulares que não estão no
PATH
shell, como você verá em outros programas não shell invocando aexecvpe()
função, e o shell não será magicamente capaz de execute (aparentemente) comandos externos comuns que outros programas não podem encontrar com o mesmo arquivoPATH
. Tudo funciona de forma auto-consistente do ponto de vista do usuário ePATH
é a ferramenta para controlar como funciona.Leitura adicional
Isso é bastante absurdo e é por isso que nenhum shell o está implementando em seu modo padrão.
A lógica do padrão e seu exemplo ilustrativo sugerem que esta foi uma tentativa fracassada de ter um built-in regular associado a um caminho, e deixar o usuário substituí-lo por ter seu próprio binário aparecendo antes dele
PATH
(por exemplo, umprintf
built-in associado com/usr/bin/printf
pode ser substituído pelo/foo/bin/printf
comando externo definindoPATH=/foo/bin:$PATH
).No entanto, o padrão acabou não exigindo isso, mas algo completamente diferente (e também inútil e inesperado).
Você pode ler mais sobre isso neste relatório de bug . Citando do texto final aceito :
FWIW, também não acho que haja nenhum shell implementando os requisitos revisados do texto aceito.
Acompanhamento vis-a-vis
echo
vsprintf
:(Abaixo,
builtin
significa "embutido especial" e "embutidos regulares" não são considerados embutidos por mim, pois não são embutidos no shell)O primeiro comitê de padronização do POSIX não conseguiu concordar em como padronizar o echo, então eles se comprometeram ao emitir que se fossem passados sinalizadores (
-e,-n,-E
, etc.\n,\c,\t
) o shell de implementação em vez de POSIX. Em vez disso, oprintf
comando foi adicionado e recebeu um comportamento bem definido. (fonte: Classic Shell Scripting, de Robbins e Beebe).Embora
printf
seja bem definido, alguns shells não possuemprintf
como comando embutido (exmksh
. ). Em vez disso, eles usamprintf
from/usr/bin/
. Isso significava que todos os scripts executados a partir desse shell imprimiriam o mesmo em um determinado sistema operacional (Ubuntu, Fedora, etc.), mas não necessariamente imprimiriam o mesmo em todos os sistemas operacionais (na verdade, muitos usuários alteraram oprintf
para/usr/bin
isso razão).Alternativamente, shells com
printf
builtin imprimiriam o mesmo independentemente do sistema operacional, mas apenas se usados como implementados para o shell. No entanto, comoprintf
o comportamento é definido pelo padrão POSIX, isso não é necessariamente uma preocupação para os programadores. No entanto, sePATH
fosse substituído por shells que usamprintf
from/usr/bin/
,printf
não seria encontrado.Embora todos os shells tenham
echo
como built-in, alguns interpretam sequências de escape diretamente (por exemploash
, ) enquanto outros (a maioria) exigem um-e
sinalizador: o comportamento não é definido pelo POSIX, mas pelo shell.Um dos principais aborrecimentos do
echo
vs.printf
é queecho
imprime novas linhas no final da string por padrão, masprintf
não.printf
requer a\n
sequência de escape para imprimir novas linhas. Por outro lado, para evitarecho
a impressão de uma nova linha, a\c
sequência de escape é necessária (potencialmente, exigindo também o-e
sinalizador).printf
érecomendado para a máxima portabilidade uma vez que o seu comportamento é definido pelo POSIX, mas pessoalmente acho que imprimir explicitamente uma nova linha no final de cada linha é bastante aborrecido (a maioria das linhas que escrevo requerem uma nova linha no final e raramente preciso suprimirecho
impressão de novas linhas). Por outro lado,echo
está sempre disponível, pois é um builtin (sem risco de não ser encontrado em $PATH) e uma simples verificação pode ser realizada para determinar se o-e
sinalizador é necessário e um alias correspondenteecho
feito:Pessoalmente, prefiro fazer isso e só usar
printf
se precisar de formatação especial.ATUALIZAÇÃO: Devo dar o devido crédito onde o crédito é devido. O código do shell acima foi retirado diretamente do
shunit2
. O crédito vai para Kate Ward e ashunit2
equipe de desenvolvimento por isso! (Bem feito ;) )Adicionando isso também ( Classic Shell Scripting de Robbins e Beebe é um ótimo livro):
Arnold Robbins e Nelson HF Beebe. Shell Scripting Clássico: Comandos Ocultos que Desbloqueiam o Poder do Unix (p. 262-5). Mídia O'Reilly. Edição Kindle.
Observe que o
command
comando faz com que o shell trate o comando e os argumentos especificados como um comando simples, suprimindo a pesquisa de função do shell. Dos Documentos IBMAssim, no meu exemplo anterior, usei o bash
builtin
em vez decommand
porque se eu o colocasse em um subshell, ele não funcionaria corretamente.Eu apoio @mosvy: parece que o texto padrão e normativo não combinam (muito absurdo mesmo).