Bash группы команд: для чего нужны фигурные скобки с точкой с запятой?

Я знаю разницу в назначении между круглыми скобками () и фигурными скобками {}, когда группирует команды в bash.

Но почему для фигурной фигурной скобки требуется точка с запятой после последней команды, тогда как для конструкции круглых скобок точка с запятой необязательна?

$ while false; do ( echo "Hello"; echo "Goodbye"; ); done
$ while false; do ( echo "Hello"; echo "Goodbye" ); done
$ while false; do { echo "Hello"; echo "Goodbye"; }; done
$ while false; do { echo "Hello"; echo "Goodbye" }; done
bash: syntax error near unexpected token `done'
$ 

Я ищу некоторое представление о том, почему это так. Я не ищу ответы, например, "потому что документация говорит так" или "потому что она была разработана именно так". Я хотел бы знать , почему он был разработан таким образом. Или, может быть, если это просто исторический артефакт?

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

  • GNU bash, версия 3.00.15 (1) -release (x86_64-redhat-linux-gnu)
  • GNU bash, версия 3.2.48 (1) -release (x86_64-apple-darwin12)
  • GNU bash, версия 4.2.25 (1) -release (x86_64-pc-linux-gnu)

Ответ 1

Потому что { и } распознаются только как специальный синтаксис, если они являются первым словом в команде.


Здесь есть два важных момента, оба из которых находятся в разделе определений руководства bash. Во-первых, это список метасимволов:

metacharacter

  

Символ, который при некорректности разделяет слова. Метасимволом является пустой или один из следующих символов: '|,' &, ';,' (, '),' <, или ' > .

  

Этот список содержит круглые скобки, но не фигурные скобки (ни курчавые, ни квадратные). Обратите внимание, что это не полный список символов со специальным значением для оболочки, но это полный список символов, разделяющих токены. Таким образом, { и } не разделяют токены и будут считаться только самими токенами, если они смежны с метасимволом, например пробелом или точкой с запятой.

Хотя фигурные скобки не являются метасимволами, они обрабатываются специально оболочкой в ​​расширении параметров (например, ${foo}) и расширение брекета (например, foo.{c,h}). Кроме этого, они просто нормальные персонажи. Нет проблем с именованием файла {ab}, например, или }{, поскольку эти слова не соответствуют синтаксису разложения параметра (для которого требуется $ до {) или расширения брекета ( которая требует по крайней мере одной запятой между { и }). В этом случае вы можете использовать { или } в качестве имени файла, не указывая при этом символы. Аналогичным образом вы можете вызвать файл if, done или time, не задумываясь о цитировании имени.

Эти последние токены являются "зарезервированными словами":

reserved word

  

Слово, которое имеет особое значение для оболочки. Большинство зарезервированных слов вводят конструкции управления потоком оболочки, такие как for и while.

  

В руководстве bash не содержится полного списка зарезервированных слов, что является неудачным, но они, безусловно, включают обозначение Posix:

!    {    }
case do   done elif else
esac fi   for  if   in
then until while

а также расширения, выполняемые bash (и некоторыми другими оболочками):

[[   ]]
function  select time

Эти слова не совпадают с встроенными (например, [), поскольку они фактически являются частью синтаксиса оболочки. Встроенные модули могут быть реализованы как функции или сценарии оболочки, но зарезервированные слова не могут быть вызваны тем, что они изменяют способ, которым оболочка анализирует командную строку.

Существует одна очень важная особенность зарезервированных слов, которая на самом деле не выделяется в руководстве bash, но очень четко выражена в Posix (из чего выше списки зарезервированных слов, за исключением time):

Это распознавание [как зарезервированное слово] происходит только тогда, когда ни один из символов не цитируется, и когда слово используется как:

  • Первое слово команды & hellip;

(Полный список мест, где распознаются зарезервированные слова, немного длиннее, но выше это довольно хорошее резюме). Другими словами, зарезервированные слова зарезервированы только в том случае, если они являются первым словом команды. И, поскольку { и } являются зарезервированными словами, они являются только специальным синтаксисом, если они являются первым словом в команде.

Пример:

ls }  # } is not a reserved word. It is an argument to `ls`
ls;}  # } is a reserved word; `ls` has no arguments

Есть много больше, о которых я мог бы написать о разборе синтаксиса, а в частности, о синтаксисе bash, но он быстро стал бы утомительным. (Например, правило о том, когда # начинает комментарий и когда это обычный символ). Примерное резюме: "не пытайтесь это дома"; действительно, единственное, что может анализировать команды оболочки, это оболочка. И не пытайтесь разобраться в этом: это просто случайная коллекция произвольных выборов и исторических аномалий, многие, но не все, основанные на необходимости не разрушать старые сценарии оболочки с новыми функциями.