Entendo que o comando command
está especificado nomais recente padrão POSIXe builtin
não é. Também percebo que ambos os comandos sãointegrados regulares(ou seja, eles podem ser substituídos por funções definidas pelo usuário). Alguns shells definem builtin
, mas não todos (por exemplo, dash
não). Quero entender por que builtin
foi introduzido em alguns shells.
Pelo que entendi, builtin
retornará apenas componentes internos especiais e regulares, mas command
retornará componentes internos especiais, depois internos regulares e, em seguida, comandos no caminho (e a -p
opção pode ser usada command
para especificá-lo para uso o padrão definido pelo shell $PATH
caso o usuário tenha modificado o $PATH
).
Por exemplo, em mksh
, vejo o seguinte:
(OBSERVAÇÃO: mksh
instalado no Ubuntu 20.04 do repo http://archive.ubuntu.com/ubuntu focal/universe amd64 mksh amd64 58-1
)
$ echo $KSH_VERSION
@(#)MIRBSD KSH R58 2020/03/27
$ which -a echo
/usr/bin/echo
/bin/echo
$ which -a printf
/usr/bin/printf
/bin/printf
$ type echo
echo is a shell builtin
$ type printf
printf is /usr/bin/printf
$ command echo 'Hello World!'
Hello World!
$ command printf 'Hello World!\n'
Hello World!
$ builtin echo 'Hello World!'
Hello World!
$ builtin printf 'Hello World!\n'
mksh: builtin: printf: not found
$ sudo cp /usr/bin/printf /usr/bin/printf.backup
$ sudo cp /bin/printf /bin/printf.backup
$ sudo rm /usr/bin/printf
$ sudo rm /bin/printf
rm: cannot remove '/bin/printf': No such file or directory
$ sudo cp /usr/bin/printf.backup ~/printf
$ echo $PATH | sed 's/:/\n/g'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
# ...remainder ommitted here for brevity
$ export PATH=~:$PATH
$ echo $PATH | sed 's/:/\n/g'
/home/my_username
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
# ...remainder ommitted here for brevity
$ command printf 'Hello World!\n'
Hello World!
$ command -p printf 'Hello World!\n'
mksh: printf: inaccessible or not found
Meu entendimento está correto? Ou fazer command
e builtin
fazer exatamente a mesma coisa (e se sim, por que foi builtin
introduzido?)? Ou existe outra diferença sutil entre command
e builtin
?
(Tentei procurar uma resposta no StackExchange, mas não encontrei nada, então se alguém puder me indicar uma resposta adequada, ficaria muito grato.)
ATUALIZAR:
Também é importante notar isso command
e builtin
"pular" a pesquisa e o uso de aliases definidos. A expansão do alias vem antes da pesquisa e avaliação do comando, assim como a expansão aritmética, variável e curinga de arquivo, na ordem de avaliação do shell POSIX. No entanto, caracteres aritméticos, variáveis e curingas são avaliados em command
e builtin
, mas não em aliases. Parece algo que os documentos deveriam mencionar.
Por exemplo:
$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ command echo $((1 + 1))
2
$ builtin echo $((3 + 1))
4
mas
$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ alias \[='echo hello'
$ [
hello
$ if builtin [ 'north' != 'south' ]; then echo 'what a world!'; fi
what a world!
ATUALIZAÇÃO 2:
Acho que também vale a pena notar que, depois de algumas pesquisas e experiências, descobri que zsh
se comportaO OPOSTO COMPLETOdo padrão POSIX e outros shells de estilo Bourne em relação ao command
comando.
Dedocumentos zsh(Seção 6.2, Modificadores de pré-comando)
comando [-pvV]
A palavra de comando é considerada o nome de um comando externo, em vez de uma função shell ou interna.
Por exemplo
$ echo ${ZSH_VERSION}
5.8
$ command cd ~
zsh: command not found: cd
$ command -p cd ~
zsh: command not found: cd
$ command echo 'hi'
hi
# `echo` is a regular builtin just like `cd` though...??!!!
$ set -o |grep posix
posixaliases off
posixargzero off
posixbuiltins off
posixcd off
posixidentifiers off
posixjobs off
posixstrings off
posixtraps off
$ cd() { echo 'I told you.'; }
$ cd
I told you.
# ????!!!!
Somente se a POSIX_BUILTINS
variável de ambiente estiver definida (use set -o posixbuiltins
) o comando command
também executará componentes internos especiais e regulares.
Por exemplo
$ echo ${ZSH_VERSION}
5.8
$ cd /
$ ls
bin dev home lib lib64 lost+found mnt proc run snap sys usr
boot etc init lib32 libx32 media opt root sbin srv tmp var
$ set -o posixbuiltins
$ command cd ~
$ ls
Desktop Downloads Pictures Templates
Documents Music Public Videos
$ command -p cd /
$ ls
bin dev home lib lib64 lost+found mnt proc run snap sys usr
boot etc init lib32 libx32 media opt root sbin srv tmp var
Por outro lado, dodocumentos bash
comando
comando [-pVv]comando [argumentos…]Executa o comando com argumentos ignorando qualquer função shell chamada comando. Somente comandos internos do shell ou comandos encontrados pesquisando o PATH são executados.
Por exemplo
$ bash --version
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$ cd /
$ ls
bin dev home lib lib64 lost+found mnt proc run snap sys usr
boot etc init lib32 libx32 media opt root sbin srv tmp var
$ set +o posix # Turn off POSIX mode
$ command cd ~
$ ls
Desktop Downloads Pictures Templates
Documents Music Public Videos
$ command -p cd /
$ ls
bin dev home lib lib64 lost+found mnt proc run snap sys usr
boot etc init lib32 libx32 media opt root sbin srv tmp var
Então zsh
se comportaO OPOSTO COMPLETOde outros shells do estilo Bourne em relação ao command
comando... estou começando a não gostar zsh
cada vez mais... comprador, cuidado (caveat emptor) Eu suponho.
ATUALIZAÇÃO 3:
Também é importante notar que ksh88
não possui um command
comando integrado. Isto foi introduzido em ksh93
. Para substituir um ksh88
, você teria que usar uma combinação estranha de alias, funções e aspas.
(Fonte: Robbins, Arnold; Rosenblatt, Bill. Aprendendo o Korn Shell: Programação Unix (p. 456). O'Reilly Media. Edição Kindle.)
Isso é consistente com a resposta de @Gilles 'SO- pare de ser mau'.
Responder1
OJustificativa POSIX paracommand
responde à maioria dos aspectos históricos da sua pergunta.
Ocomandoutilitário é um pouco semelhante ao shell da Oitava Ediçãoconstruídas emcomando, mas desdecomandotambém vai ao sistema de arquivos para procurar utilitários, o nomeconstruídas emnão seria intuitivo.
(…) Ocomando -ve-Vopções foram adicionadas para satisfazer os requisitos dos usuários que atualmente são atendidos por três utilitários históricos diferentes:tipono shell do System V,de ondeno KornShell equalno shell C.
NoOitava Edição sh, o builtin
builtin foi documentado apenas como ignorando funções:
Execute o comando especial integrado (como break) independentemente das funções definidas com o mesmo nome.
Os aliases ainda não existiam (e quando apareceram, havia diferentes mecanismos para contorná-los). Se você quisesse ignorar uma função para executar um comando externo, você poderia fornecer seu caminho completo, que tinha a vantagem de especificar exatamente o que você queria executar caso houvesse vários executáveis com esse nome no caminho de busca do comando. A portabilidade entre sistemas onde o caminho completo para um comando pode ser diferente não era uma preocupação generalizada. O mesmo builtin
aconteceu com a única coisa que realmente não poderia ser feita de outra maneira.
Mais tarde, o POSIX veio e adicionou uma forma padrão de ignorar uma função. Neste contexto, a portabilidade para sistemas onde os comandos externos estavam em locais diferentes era uma grande preocupação, por isso builtin
não era suficiente, daí o novo command
que ignora funções (e aliases, uma vez que command foo
coloca foo
numa posição onde os aliases não são expandidos) e encontra padrão comandos. (Também hoje o ksh tem um chamado interno builtin
que faz algo completamente diferente, mas não sei se veio antes ou depois da criação do POSIX command
.) No entanto, command
propositalmente não pula comandos internos, novamente devido a questões de portabilidade: se um sh programas invocam um comando padrão, cabe ao sistema operacional escolher se esse comando pode ser fornecido como integrado. command
cancela o comportamento “integrado especial” de componentes internos especiais, novamente para que o aplicativo não precise saber se está invocando um componente interno ou não.
Não sei por que o zsh command
ignora os componentes internos quando não está no modo POSIX (especificamente, quando oposix_builtins
opçãonão está definido). Sua implementação atual command
remonta a uma mudança em maio de 1996 lançada emzsh 2.6 beta 20(“remover -, exec, noglob e comando da lista de palavras reservadas”). Como essa implementação já tinha um tratamento diferente para o modo POSIX, presumo que fosse para compatibilidade retroativa com uma versão anterior do zsh, mas não investiguei mais. Pode ser deliberado porque, se posix_builtins
não estiver definido, os integrados não serão necessariamente compatíveis com POSIX e, portanto, é melhor não invocá-los se um aplicativo usar o command
comando especificamente POSIX.