Понимание встроенных команд оболочки

12

В руководстве по bash написано, что

Builtin commands are contained >>> within <<< the shell itself

Кроме того, этот ответ гласит, что

A built-in command is simply a command that the shell carries out itself,
instead of interpreting it as a request to load and run some
>>> other program <<<

Когда я бег compgen -bна bash 4.4, я получаю список всех оболочки встроены командами. Я вижу, например, что [и killперечислены как встроенные оболочки. Но их фактическое местоположение:

/usr/bin/[
/bin/kill

Я думал, что это builtinозначает, что команда скомпилирована в /bin/bashисполняемый файл. Итак, что меня действительно смущает: пожалуйста, исправьте меня, но как может быть отдельная команда builtin, если она на самом деле не является частью оболочки?

manifestor
источник
1
Некоторые команды изначально существовали как отдельные утилиты. В настоящее время они присутствуют для соответствия стандарту POSIX, мобильности, а также для обратной совместимости. Оболочки реализуют некоторые встроенные для производительности. Может быть и другая причина, но об этом без лишних подробностей.
Сергей Колодяжный
1
Другая причина, о которой я мог подумать, заключается в том, что некоторые встроенные команды необходимы именно для оболочки, например, execдля манипулирования дескрипторами файлов и eval для оценки команд. Они не нужны как отдельные команды
Сергей Колодяжный

Ответы:

16

Команды, встроенные в оболочку, часто встроены из-за увеличения производительности, которое это дает. Например, внешний вызов printfвыполняется медленнее, чем встроенный printf.

Поскольку некоторые утилиты не нужно встраивать, если они не являются специальными, например cd, они также предоставляются в качестве внешних утилит. Это делается для того, чтобы сценарии не ломались, если они интерпретируются оболочкой, которая не предоставляет встроенный эквивалент.

Некоторые встроенные функции оболочки также предоставляют расширения для внешней эквивалентной команды. Баш printf, например, умеет делать

$ printf -v message 'Hello %s' "world"
$ echo "$message"
Hello world

(вывод в переменную), что внешнее /usr/bin/printfпросто не сможет сделать, так как у него нет доступа к переменным оболочки в текущем сеансе оболочки (и они не могут их изменить).

Встроенные утилиты также не имеют ограничения, что их расширенная командная строка должна быть короче определенной длины. дела

printf '%s\n' *

Поэтому безопасно, если printfэто встроенная команда оболочки. Ограничение длины командной строки исходит из execve()функции библиотеки C, используемой для выполнения внешней команды. Если командная строка и текущая среда больше ARG_MAXбайтов (см. getconf ARG_MAXВ оболочке), вызов execve()завершится ошибкой. Если утилита встроена в оболочку, execve()вызывать не нужно.

Встроенные утилиты имеют приоритет над утилитами, найденными в $PATH. Чтобы отключить встроенную команду bash, используйте, например,

enable -n printf

Существует небольшой список утилит, которые необходимо встроить в оболочку (взят из списка специальных встроенных модулей стандарта POSIX )

break
colon (:)
continue
dot (.)
eval
exec
exit
export
readonly
return
set
shift
times
trap
unset

Они должны быть встроены, так как они напрямую управляют средой и потоком программы текущего сеанса оболочки. Внешняя утилита не сможет этого сделать.

Интересно, cdчто не входит в этот список, но POSIX говорит об этом следующее :

Поскольку cdвлияет на текущую среду выполнения оболочки, она всегда предоставляется как обычная встроенная оболочка. Если он вызывается в подоболочке или в отдельной среде выполнения утилит, например, одной из следующих:

(cd /tmp)
nohup cd
find . -exec cd {} \;

это не влияет на рабочий каталог среды вызывающего.

Поэтому я предполагаю, что «специальные» встроенные модули не могут иметь внешних аналогов, в то время как cdтеоретически могут иметь (но это не очень поможет).

Кусалананда
источник
IIRC, chdir/ cdбыли внешними двоичными файлами в очень ранних версиях Unix / pre-Unix до того, как forkбыл представлен.
Xophmeister
@Xophmeister Solaris 11.4 (бета-версия) все еще имеет /usr/bin/cd, но на самом деле он не изменит текущий рабочий каталог. Его руководство гласит: /usr/bin/cdне влияет на процесс вызова, но может использоваться для определения, может ли данный каталог быть установлен в качестве текущего каталога.
Кусалананда
2
Еще одна, довольно специфическая причина для встроенных функций: встроенная система killтакже хороша, потому что ей не нужно форкать другой процесс, хорошо, если вы достигли ограничения по количеству процессов.
Дероберт
7

Вы (очень понятно) смущает тот факт , что некоторые встроенные функции существуют как в качестве встроенных команд и в качестве внешних команд. Итак, хотя вы правы в том, что, например, есть /bin/[команда, это не значит, что ее «фактическое местоположение» находится в /bin.

Любой простой способ проверить это - запустить typeс -aпереключателем, который покажет все доступные экземпляры команды. В моей системе Arch это показывает:

$ type -a [
[ is a shell builtin
[ is /sbin/[
[ is /usr/sbin/[
[ is /usr/bin/[

Обратите внимание , что /sbin, /usr/sbinи /binвсе символические ссылки , указывающие /usr/bin, так что есть только один внешний [:

$ readlink -f /usr/sbin /sbin /bin/
/usr/bin
/usr/bin
/usr/bin

Как вы можете видеть, [это как встроенная, так и внешняя команда, и то же самое верно для различных других встроенных команд оболочки. Однако это не меняет того факта, что они также являются встроенными в оболочку, скомпилированными в саму оболочку.

Тердон
источник
почему дистрибутив предоставить отдельную внешнюю команду для уже существующей внутренней команды? почему они дублируют?
LoveWithMaths
1
@linuxuser Некоторые из этих утилит требуются POSIX, и вы не можете знать, будет ли оболочка, которую использует пользователь, также предоставлять встроенную функцию. Не думайте о них как о внутренней команде ОС, они являются только внутренними командами оболочки, и оболочка может измениться.
Terdon
У меня есть 1 сомнение сейчас, если внутренние команды предоставляются оболочкой; тогда кто предоставляет внешние команды? как я наблюдал много команд, которые доступны как внутренние, так и внешние команды, но я явно не устанавливал их; так кто предоставляет внешнюю команду? Дистро предоставляет их правильно?
LoveWithMaths
@linuxuser зависит от команды и операционной системы. Например, на моем Arch Linux, /bin/printfустанавливается на coreutilsкорпусе и /bin/killна util-linux.
Terdon
Прошу прощения, но мне все еще неясно, что из вышеперечисленного предоставляется дистрибутивом? и что относительно другого, который не обеспечен дистрибутивом тогда, кто обеспечивает это.
LoveWithMaths