Diferencia entre el comando `comando` y el comando `integrado` en Shell Scripting

Diferencia entre el comando `comando` y el comando `integrado` en Shell Scripting

Entiendo que el comando commandestá especificado en elúltimo estándar POSIXy builtinno lo es. También me doy cuenta de que ambos comandos sonincorporaciones regulares(es decir, pueden sobrescribirse mediante funciones definidas por el usuario). Algunos shells definen builtin, pero no todos (por ejemplo, dashno). Quiero entender por qué builtinse introdujo en algunos shells.

Hasta donde tengo entendido, builtinsolo devolverá elementos integrados especiales y luego regulares, pero commanddevolverá elementos integrados especiales, luego integrados regulares y luego comandos en la ruta (y -pse puede usar el modificador commandpara especificar su uso). el valor predeterminado definido por el shell $PATHen caso de que el usuario haya modificado el $PATHarchivo ).

Por ejemplo, en mksh, veo lo siguiente:

(NOTA: mkshinstalado en Ubuntu 20.04 desde el repositorio 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

¿Es correcto mi entendimiento? ¿O hacer commandy builtinhacer exactamente lo mismo (y si es así, por qué se builtinintrodujo?). ¿O hay otra diferencia sutil entre commandy builtin?

(Intenté buscar una respuesta en StackExchange, pero no encontré nada, por lo que si alguien puede indicarme una respuesta adecuada, estaría muy agradecido).

ACTUALIZAR:

También vale la pena señalar que command" builtinomita" la búsqueda y el uso de alias definidos. La expansión de alias viene antes de la búsqueda y evaluación de comandos, al igual que la expansión aritmética, de variables y de comodines de archivos, en el orden de evaluación del shell POSIX. Sin embargo, la aritmética, las variables y los comodines se evalúan dentro de commandy builtin, pero no los alias. Parece algo que los documentos deberían mencionar.

Por ejemplo:

$ 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

pero

$ 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!

ACTUALIZACIÓN 2:

Creo que también vale la pena señalar que, después de investigar y experimentar un poco, descubrí que zshse comportaTODO LO CONTRARIOdel estándar POSIX y otros shells de estilo Bourne con respecto al commandcomando.

Desde eldocumentos zsh(Sección 6.2, Modificadores de precomando)

comando [-pvV]
La palabra de comando se considera el nombre de un comando externo, en lugar de una función de shell o incorporada.

Por ejemplo

$ 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.
# ????!!!!

Solo si la POSIX_BUILTINSvariable de entorno está configurada (use set -o posixbuiltins), el comando commandtambién ejecutará funciones integradas especiales y regulares.

Por ejemplo

$ 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 otro lado, desde eldocumentos bash

dominio
comando [-pVv]comando [argumentos…]

Ejecuta un comando con argumentos ignorando cualquier función del shell denominada comando. Sólo se ejecutan los comandos integrados del shell o los comandos encontrados al buscar en la RUTA.

Por ejemplo

$ 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

Así zshse comportaTODO LO CONTRARIOde otros shells estilo Bourne con respecto al commandcomando... Estoy empezando a disgustarme zshcada vez más... cuidado con el comprador (advertencia emptor) Supongo.

ACTUALIZACIÓN 3:

También vale la pena señalar que ksh88no tiene un commandcomando incorporado. Esto fue introducido en ksh93. Para reemplazar un archivo integrado ksh88, tendría que usar una extraña combinación de alias, funciones y comillas.

(Fuente: Robbins, Arnold; Rosenblatt, Bill. Aprendizaje de Korn Shell: programación en Unix (p. 456). Medios O'Reilly. Versión Kindle.)

Esto es consistente con la respuesta de @Gilles 'SO- deja de ser malvado'.

Respuesta1

ElJustificación POSIX paracommandresponde a la mayoría de los aspectos históricos de su pregunta.

EldominioLa utilidad es algo similar al shell de la octava edición.incorporadocomando, pero desdedominiotambién va al sistema de archivos para buscar utilidades, el nombreincorporadoNo sería intuitivo.

(…) Eldominio -vy-VSe agregaron opciones para satisfacer los requisitos de los usuarios que actualmente cumplen tres utilidades históricas diferentes:tipoen el shell del Sistema V,De dóndeen KornShell, ycualen el caparazón C.

En elOctava edición sh, el builtinincorporado fue documentado como simplemente omitiendo funciones:

Ejecute el comando especial integrado (como break) independientemente de las funciones definidas con el mismo nombre.

Los alias aún no existían (y cuando aparecieron, existían diferentes mecanismos para evitarlos). Si desea omitir una función para ejecutar un comando externo, puede proporcionar su ruta completa, lo que tiene la ventaja de especificar exactamente lo que desea ejecutar en caso de que haya varios ejecutables con ese nombre en la ruta de búsqueda de comandos. La portabilidad entre sistemas donde la ruta completa a un comando podría ser diferente no era una preocupación generalizada. También lo builtinhizo la única cosa que realmente no se podía hacer de otra manera.

Más tarde, llegó POSIX y agregó una forma estándar de omitir una función. En este contexto, la portabilidad a sistemas donde los comandos externos estaban en diferentes ubicaciones era una gran preocupación, por lo que builtinno era suficiente, de ahí la novedad commandque omite funciones (y alias, ya que command foocoloca fooen una posición donde los alias no se expanden) y encuentra estándares comandos. (Además, hoy en día, ksh tiene un comando integrado llamado builtinque hace algo completamente diferente, pero no sé si apareció antes o después de la creación de POSIX command). Sin embargo, commandintencionalmente no omite los comandos integrados, nuevamente debido a problemas de portabilidad: si un sh Un programa invoca un comando estándar, es decisión del sistema operativo si este comando se puede proporcionar de forma integrada. commandcancela el comportamiento de “incorporación especial” de las funciones integradas especiales, nuevamente para que la aplicación no necesite saber si está invocando una función incorporada o no.

No sé por qué zsh commandomite las funciones integradas cuando no está en modo POSIX (específicamente, cuando elposix_builtinsopciónno está configurado). Su implementación actual commandse remonta a un cambio en mayo de 1996 publicado enzsh 2.6 beta 20("eliminar -, exec, noglob y comando de la lista de palabras reservadas"). Dado que esa implementación ya tenía un manejo diferente para el modo POSIX, supongo que era por compatibilidad con una versión anterior de zsh, pero no he investigado más. Puede ser deliberado porque si posix_builtinsno está configurado, las funciones integradas no son necesariamente compatibles con POSIX y, por lo tanto, es mejor no invocarlas si una aplicación usa el commandcomando específicamente POSIX.

información relacionada