Разница между командой `command` и командой `builtin` в скриптах оболочки

Разница между командой `command` и командой `builtin` в скриптах оболочки

Я понимаю, что команда commandуказана вновейший стандарт POSIXи builtinнет. Я также понимаю, что обе командыобычные встроенные функции(т.е. они могут быть перезаписаны пользовательскими функциями). Некоторые оболочки определяют builtin, но не все (например, dashне делает). Я хочу понять, почему builtinбыл введен в некоторых оболочках.

Насколько я понимаю, builtinвернет только специальные, а затем обычные встроенные функции, но commandвернет специальные встроенные функции, затем обычные встроенные функции, затем команды по пути (и переключатель -pможно использовать с , commandчтобы указать, что нужно использовать значение по умолчанию, определенное оболочкой, $PATHв случае, если пользователь изменил $PATH).

Например, в mkshя вижу следующее:

(ПРИМЕЧАНИЕ: mkshустановлен на Ubuntu 20.04 из репозитория 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

Правильно ли я понимаю? Или делать commandи builtinделать одно и то же (и если да, то почему было builtinвведено?)? Или есть еще одно тонкое различие между commandи builtin?

(Я пытался найти ответ на StackExchange, но ничего не нашел, поэтому, если кто-то может указать мне подходящий ответ, я буду очень признателен.)

ОБНОВЛЯТЬ:

Также стоит отметить, что commandи builtin"пропустить" поиск и использование определенных псевдонимов. Расширение псевдонимов предшествует поиску и оценке команд, как и арифметическое, переменное и расширение подстановочных знаков файлов в порядке оценки оболочки POSIX. Однако арифметические, переменные и подстановочные знаки оцениваются внутри commandи builtin, но не псевдонимы. Похоже, что в документах это должно быть упомянуто.

Например:

$ 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

но

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

ОБНОВЛЕНИЕ 2:

Я думаю, стоит также отметить, что после некоторых исследований и экспериментов я обнаружил, что zshведет себяПОЛНАЯ ПРОТИВОПОЛОЖНОСТЬстандарта POSIX и других оболочек в стиле Bourne в отношении commandкоманды.

Изzsh документы(Раздел 6.2, Модификаторы прекоманд)

команда [-pvV]
Под командным словом понимается имя внешней команды, а не функции оболочки или встроенной функции.

Например

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

Только если POSIX_BUILTINSзадана переменная окружения (используйте set -o posixbuiltins), команда commandтакже выполнит специальные и обычные встроенные функции.

Например

$ 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

С другой стороны, сbash документы

команда
команда [-pVv]команда [аргументы …]

Запускает команду с аргументами, игнорируя любую функцию оболочки с именем command. Выполняются только встроенные команды оболочки или команды, найденные путем поиска в PATH.

Например

$ 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

Так zshсебя ведетПОЛНАЯ ПРОТИВОПОЛОЖНОСТЬдругих оболочек в стиле Борна в отношении commandкоманд...мне они начинают не нравиться zshвсе больше и больше...покупатели будьте осторожны (пусть покупатель будет бдителен) Я полагаю.

ОБНОВЛЕНИЕ 3:

Также стоит отметить, что ksh88не имеет commandвстроенной команды. Это было введено в ksh93. Чтобы заменить встроенную в ksh88, вам пришлось бы использовать неуклюжую комбинацию псевдонимов, функций и кавычек.

(Источник: Роббинс, Арнольд; Розенблатт, Билл. Изучение оболочки Korn: программирование в Unix (стр. 456). O'Reilly Media. Издание Kindle.)

Это согласуется с ответом @Gilles «ТАК- хватит быть злым».

решение1

TheОбоснование POSIX дляcommandотвечает на большинство исторических аспектов вашего вопроса.

Theкомандаутилита чем-то похожа на оболочку восьмого изданиявстроенныйкоманда, но так каккомандатакже обращается к файловой системе для поиска утилит, имявстроенныйне было бы интуитивно понятным.

(…)команда иБыли добавлены опции для удовлетворения потребностей пользователей, которые в настоящее время выполняются тремя различными историческими утилитами:типв оболочке System V,откудав KornShell, икоторыйв оболочке C.

вВосьмое издание ш, builtinвстроенная функция была задокументирована как просто обходящая функции:

Выполнить встроенную специальную команду (например, break) независимо от функций, определенных с тем же именем.

Псевдонимов еще не существовало (и когда они появились, существовали различные механизмы их обхода). Если вы хотели обойти функцию для выполнения внешней команды, вы могли указать ее полный путь, что имело преимущество указания того, что именно вы хотели выполнить, в случае, если в пути поиска команд было несколько исполняемых файлов с таким именем. Переносимость между системами, где полный путь к команде мог быть другим, не была широко распространенной проблемой. Как и builtinединственная вещь, которую нельзя было сделать по-другому.

Позже появился POSIX и добавил стандартный способ обхода функции. В этом контексте переносимость в системы, где внешние команды находились в разных местах, была очень важна, поэтому ее builtinбыло недостаточно, поэтому появился новый command, который обходит функции (и псевдонимы, поскольку command fooпомещает fooв положение, в котором псевдонимы не раскрываются) и находит стандартные команды. (Также сегодня в ksh есть встроенная команда , builtinкоторая делает что-то совершенно другое, но я не знаю, появилась ли она до или после создания POSIX command.) Однако commandнамеренно не пропускает встроенные команды, опять же из-за проблем переносимости: если программа sh вызывает стандартную команду, то операционная система сама решает, может ли эта команда быть предоставлена ​​как встроенная. commandотменяет поведение «специальной встроенной» специальных встроенных команд, опять же, чтобы приложению не нужно было знать, вызывает ли оно встроенную команду или нет.

Я не знаю, почему zsh commandобходит встроенные функции, когда не находится в режиме POSIX (в частности, когдаposix_builtinsвариантне установлено). Его текущая реализация commandвосходит к изменению в мае 1996 года, выпущенному взш 2.6 бета 20(«удалить -, exec, noglob и command из списка зарезервированных слов»). Поскольку эта реализация уже имела другую обработку для режима POSIX, я предполагаю, что это было сделано для обратной совместимости с более ранней версией zsh, но я не исследовал это более подробно. Это может быть сделано намеренно, поскольку если posix_builtinsне установлено, встроенные функции не обязательно совместимы с POSIX, и поэтому лучше не вызывать их, если приложение использует commandкоманду POSIX.

Связанный контент