Когда я выполняю эти две команды, я добираюсь
$ type cd
cd is a shell builtin
$ type if
if is a shell keyword
Этому ясно показывают это cd
встроенная оболочка и if
ключевое слово оболочки. Таким образом, каково различие между встроенной оболочкой и ключевым словом?
Существует сильное различие между встроенным и ключевым словом в способе, которым Bash анализирует Ваш код. Прежде чем мы будем говорить о различии, давайте перечислим все ключевые слова и builtins:
Builtins:
$ compgen -b
. : [ alias bg bind break
builtin caller cd command compgen complete compopt
continue declare dirs disown echo enable eval
exec exit export false fc fg getopts
hash help history jobs kill let local
logout mapfile popd printf pushd pwd read
readarray readonly return set shift shopt source
suspend test times trap true type typeset
ulimit umask unalias unset wait
Ключевые слова:
$ compgen -k
if then else elif fi case
esac for select while until do
done in function time { }
! [[ ]] coproc
Заметьте это, например [
встроенное и это [[
ключевое слово. Я буду использовать эти два для иллюстрирования различия ниже, так как они - известные операторы: все знают их и регулярно используют их (или если).
Ключевое слово просканировано и понято под Bash очень рано в его парсинге. Это позволяет, например, следующее:
string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
echo "The string is non-empty"
fi
Это хорошо работает, и Bash счастливо произведет
The string is non-empty
Обратите внимание, что я не заключил в кавычки $string_with_spaces
. Принимая во внимание, что следующее:
string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
echo "The string is non-empty"
fi
шоу, что Bash не счастлив:
bash: [: too many arguments
Почему это работает с ключевыми словами а не с builtins? потому что, когда Bash анализирует код, он видит [[
который является ключевым словом и понимает очень рано, что это является особенным. Таким образом, это будет искать закрытие ]]
и будет рассматривать внутреннюю часть специальным способом. Встроенное (или команда) рассматривают как фактическую команду, которая будет названной с аргументами. В этом последнем примере удар понимает, что должен выполнить команду [
с аргументами (показанный на строку):
-n
some
spaces
here
]
начиная с переменного расширения, удаления кавычки, расширения пути и разделения слова происходит. Команда [
оказывается созданным в оболочке, таким образом, она выполняет его с этими аргументами, который приводит к ошибке, следовательно жалоба.
На практике Вы видите, что это различие допускает сложное поведение, которое не было бы возможно с builtins (или команды).
Все еще на практике, как можно возможно отличить встроенное от ключевого слова? это - забавный эксперимент для выполнения:
$ a='['
$ $a -d . ]
$ echo $?
0
Когда Bash анализирует строку $a -d . ]
, это не видит ничего специального (т.е. никакие псевдонимы, никакие перенаправления, никакие ключевые слова), таким образом, это просто выполняет переменное расширение. После переменных расширений это видит:
[ -d . ]
так выполняет (встроенную) команду [
с аргументами -d
, .
и ]
, который, конечно, верно (это только тестирует ли .
каталог).
Теперь взгляд:
$ a='[['
$ $a -d . ]]
bash: [[: command not found
О. Поэтому, когда Bash видит эту строку, он не видит ничего специального, и следовательно разворачивает все переменные и в конечном счете видит:
[[ -d . ]]
В это время, расширения псевдонима и сканирование ключевого слова долго выполнялся и не будет выполненным больше, таким образом, Bash пытается найти команду названной [[
, не находит его и жалуется.
В том же направлении:
$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found
и
$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found
Расширение псевдонима является чем-то довольно специальным также. Вы все сделали следующее, по крайней мере, однажды:
$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found
Обоснование является тем же: расширение псевдонима происходит задолго до переменного расширения и удаления кавычки.
Ключевое слово v.s. Псевдоним
Теперь то, что Вы думаете, происходит, если мы определяем псевдоним, чтобы быть ключевым словом?
$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0
О, это работает! таким образом, псевдонимы могут использоваться для искажения ключевых слов! хороший знать.
Заключение: builtins действительно ведут себя как команды: они соответствуют действию, выполняемому с аргументами, которые подвергаются прямому переменному расширению и разделению слова и globbing. Это действительно точно так же, как имеет внешнюю команду где-нибудь в /bin
или /usr/bin
это называют с аргументами, данными после переменного расширения и т.д. Обратите внимание что, когда я говорю, что это действительно точно так же, как имеет внешнюю команду, которую я только имею в виду относительно аргументов, разделения слова, globbing, переменного расширения, и т.д. Встроенное может изменить внутреннее состояние оболочки!
Ключевые слова, с другой стороны, просканированы и поняты очень рано и допускают сложное поведение оболочки: оболочка сможет запретить разделение слова или расширение пути и т.д.
Теперь посмотрите на список builtins и ключевых слов и попытки выяснить почему некоторая потребность быть ключевыми словами.
!
ключевое слово. Кажется, что было бы возможно подражать своему поведению с функцией:
not() {
if "$@"; then
return false
else
return true
fi
}
но это запретило бы конструкции как:
$ ! ! true
$ echo $?
0
или
$ ! { true; }
echo $?
1
То же для time
: более способно иметь его ключевое слово так, чтобы это могло команды составного объекта комплекса времени и конвейеры с перенаправлениями:
$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null
Если time
где простая команда (даже встроенный), это только видело бы аргументы grep
, ^#
и /home/gniourf/.bashrc
, время это и затем его вывод прошло бы остающиеся части конвейера. Но с ключевым словом, Bash может обработать все! это может time
полный конвейер, включая перенаправления! Если time
если бы простой команда были, то мы не могли сделать:
$ time { printf 'hello '; echo world; }
Попробуйте:
$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'
Попытайтесь зафиксировать (?) его:
$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory
Безнадежный.
Ключевое слово по сравнению с псевдонимом?
$ alias mytime=time
$ alias myls=ls
$ mytime myls
То, что Вы думаете, происходит?
Действительно, встроенное похоже на команду, за исключением того, что она создается в оболочке, тогда как ключевое слово - что-то, что допускает сложное поведение! мы можем сказать, что это - часть грамматики оболочки.
man bash
называет их SHELL BUILTIN COMMANDS
. Так, "оболочка, встроенная", точно так же, как нормальная команда, как grep
, и т.д., но вместо того, чтобы содержаться в отдельном файле, это встроено в сам удар . Это заставляет их работать более эффективно, чем внешние команды.
А ключевое слово также "трудно кодируется в Bash, но в отличие от встроенного, ключевое слово не является сам по себе командой, но подблоком конструкции команды". Я интерпретирую это, чтобы означать, что ключевые слова не имеют одной только никакой функции, но требуют, чтобы команды сделали что-либо. (Из ссылки другие примеры for
, while
, do
, и !
, и существует больше в мой ответ к Вашему другому вопросу.)
Руководство командной строки, которое идет с Ubuntu, не дает определение ключевых слов, однако руководство онлайн (см. заметку на полях), и POSIX спецификации стандарта Командного языка Shell, назовите их "Зарезервированными словами", и оба предоставляют списки тех. Из стандарта POSIX:
Это распознавание должно только произойти, когда ни один из символов не будет заключен в кавычки и когда слово используется как:
Первая команда
Первое слово после одного из зарезервированных слов кроме случая, поскольку, или в
Третье слово в команде случая (только в допустимо в этом случае),
Третье слово в для команды (только в и делают, допустимо в этом случае),
Ключ здесь - то, что ключевые слова/зарезервированные слова имеют особое значение, потому что они упрощают синтаксис оболочки, служите для передачи сигналов об определенных блоках кода, таких как циклы, составные команды, переходя (если/заключать в корпус) операторы, и т.д. Они позволяют формировать командные операторы, но собой - ничего не делают, и на самом деле если Вы вводите ключевые слова такой как for
, until
, case
- оболочка будет ожидать законченное высказывание, иначе - синтаксическая ошибка:
$ for
bash: syntax error near unexpected token `newline'
$
На уровне исходного кода зарезервированные слова для удара определяются в parese.y, в то время как создано-ins выделили целый каталог им.
Индексные шоу GNU [
как зарезервированное слово, однако это - на самом деле встроенная команда. [[
в отличие от этого, зарезервированное слово.
См. также: Различия между ключевым словом, зарезервированным словом, и встроенный?