Каково различие между встроенной оболочкой и ключевым словом оболочки?

Когда я выполняю эти две команды, я добираюсь

$ type cd
cd is a shell builtin
$ type if
if is a shell keyword

Этому ясно показывают это cd встроенная оболочка и if ключевое слово оболочки. Таким образом, каково различие между встроенной оболочкой и ключевым словом?

32
задан 14 June 2017 в 06:22

3 ответа

Существует сильное различие между встроенным и ключевым словом в способе, которым 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

То, что Вы думаете, происходит?


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

44
ответ дан 23 November 2019 в 00:38

man bash называет их SHELL BUILTIN COMMANDS. Так, "оболочка, встроенная", точно так же, как нормальная команда, как grep, и т.д., но вместо того, чтобы содержаться в отдельном файле, это встроено в сам удар . Это заставляет их работать более эффективно, чем внешние команды.

А ключевое слово также "трудно кодируется в Bash, но в отличие от встроенного, ключевое слово не является сам по себе командой, но подблоком конструкции команды". Я интерпретирую это, чтобы означать, что ключевые слова не имеют одной только никакой функции, но требуют, чтобы команды сделали что-либо. (Из ссылки другие примеры for, while, do, и !, и существует больше в мой ответ к Вашему другому вопросу.)

9
ответ дан 23 November 2019 в 00:38

Руководство командной строки, которое идет с Ubuntu, не дает определение ключевых слов, однако руководство онлайн (см. заметку на полях), и POSIX спецификации стандарта Командного языка Shell, назовите их "Зарезервированными словами", и оба предоставляют списки тех. Из стандарта POSIX:

Это распознавание должно только произойти, когда ни один из символов не будет заключен в кавычки и когда слово используется как:

  • Первая команда

  • Первое слово после одного из зарезервированных слов кроме случая, поскольку, или в

  • Третье слово в команде случая (только в допустимо в этом случае),

  • Третье слово в для команды (только в и делают, допустимо в этом случае),

Ключ здесь - то, что ключевые слова/зарезервированные слова имеют особое значение, потому что они упрощают синтаксис оболочки, служите для передачи сигналов об определенных блоках кода, таких как циклы, составные команды, переходя (если/заключать в корпус) операторы, и т.д. Они позволяют формировать командные операторы, но собой - ничего не делают, и на самом деле если Вы вводите ключевые слова такой как for, until, case - оболочка будет ожидать законченное высказывание, иначе - синтаксическая ошибка:

$ for
bash: syntax error near unexpected token `newline'
$  

На уровне исходного кода зарезервированные слова для удара определяются в parese.y, в то время как создано-ins выделили целый каталог им.

Заметка на полях

Индексные шоу GNU [ как зарезервированное слово, однако это - на самом деле встроенная команда. [[ в отличие от этого, зарезервированное слово.

См. также: Различия между ключевым словом, зарезервированным словом, и встроенный?

1
ответ дан 23 November 2019 в 00:38

Другие вопросы по тегам:

Похожие вопросы: