Я понимаю, что команда 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.