Какая польза от скобок `()` в определении функции оболочки?


20

Функция определяется как:

do_something () {
    do it
}

Я мог понять его имя «do_something» и фигурную скобку для инкапсуляции кода действий, но не могу понять, для чего ()здесь, так как в скриптах Bash нет именованных параметров. Возможно, было бы лучше и проще определить его как

do_something {
  do it
}

Который не конфликтует с его текущим синтаксисом, а еще больше заявляет, что нет именованных параметров. Какая тут польза ()?


Ответы:


44

Без этого ()синтаксис действительно был бы неоднозначным.

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

do_something {
    # one or more commands go here
}

Вы сказали, что это «не противоречит текущему синтаксису», но это так! Обратите внимание, что вы не получите никакой синтаксической ошибки при попытке запустить первую строку этого . Вы получаете ошибку, но это не ошибка синтаксиса. Вторая строка, с }, является синтаксической ошибкой, но первая строка - нет. Вместо этого do_something {пытается выполнить команду, которая называется, do_somethingи передать {в качестве аргумента эту команду:

$ do_something {
do_something: command not found

Если команда уже вызвана do_something, вы ее запускаете. Если функция уже вызвана do_something, вы вызываете ее . В целом важно, чтобы синтаксис был однозначным, но также важно, чтобы было возможно переопределить функцию, не вызывая ее случайно. Определение функции и ее вызов не должны выглядеть одинаково.

Как оболочка лечит {и (.

Как type {скажет вам, {это ключевое слово оболочки. Это делает это как [[. Если используется в ситуации, когда в противном случае это была бы команда, {несет особую семантику. В частности, он выполняет группирование команд. В других ситуациях, однако, он может использоваться без экранирования для обозначения буквального {символа. Это включает в себя ситуацию передачи его в качестве второго или последующего слова команды.

Конечно, Bash мог быть разработан так, чтобы относиться {иначе, чем в настоящее время. Однако его синтаксис больше не был бы совместим с оболочкой POSIX, и Bash на самом деле не был бы оболочкой в ​​стиле Борна и не мог бы выполнять много сценариев оболочки.

Напротив, (это метасимвол оболочки. Он всегда относился особенно , если он появляется в команде и не котируется (с ' ', " "или \). Таким образом, в синтаксисе нет двусмысленности:

do_something() {
    # one or more commands go here
}

Это не может означать ничего другого. Если у Bash нет функций, это будет синтаксическая ошибка, по той же причине echo foo(bar)синтаксическая ошибка.

Если вам действительно не нравится ()запись, вы можете использовать ключевое слово functionи опустить его, как упоминает sudodus . Обратите внимание, что это не является частью синтаксиса для определения функций в большинстве других оболочек в стиле Борна - и в некоторых он поддерживается, но функции, определенные таким образом, имеют различную семантику - и поэтому сценарий, который его использует, не будет переносимым. (Причина, по которой этот синтаксис может быть однозначным, заключается в том, что functionоно само по себе является ключевым словом в Bash, которое означает, что все, что следует за ним, является началом определения функции.)

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


1
На более высоком уровне речь идет о человеческих ожиданиях и удобочитаемости. В большинстве языков, особенно в C, Fortran и других, распространенных во время разработки языков сценариев bash, функция / подпрограмма всегда имеет список аргументов, возможно, пустой, который заключен в скобки. Измените эту парадигму, и вам будет сложнее понимать язык.
jamesqf

15

()Лексема сказать интерпретатор оболочки , который вы объявляете функцию.

$ do_something () { echo 'do it'; } ; do_something
do it

Альтернатива в bashтом,function

function do_something {
 echo 'do it'
}

или в качестве одной строки вы можете проверить

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it

2
functionКлючевое слово является предварительно POSIX КШ-изма , что Баш опоры для обратной совместимости; однако, bash поддерживает это плохо (не делая переменные локальными по умолчанию, как это делал старый ksh в функциях, объявленных в этой форме). Следовательно, он не идеален для нового кода. См. Wiki.bash-hackers.org/scripting/obsolete
Чарльз Даффи

@CharlesDuffy, спасибо за это объяснение :-)
sudodus

1
@CharlesDuffy Вы говорите, что ksh и bash принимают некоторый синтаксис, который делает переменную локальной в ksh, но глобальной в bash? Это не звучит правильно. Насколько я понимаю, частично основываясь на этом посте и проверке (в ksh93), ksh делает переменные локальными в меньшем количестве ситуаций, чем в bash, и никогда больше. В bash, typesetбез -gфункции всегда объявляет локальную переменную. С functionключевым словом, Баш и КШ переменные области видимости таким же образом , не они?
Элия ​​Каган

@EliahKagan Я думаю , что Чарльз был ссылкой на это было продемонстрировано в ответ Жиль в здесь
Сергий Kolodyazhnyy

9

Как очень один из вас, истинно, в стиле скобки !

Рассмотрим другие, совершенно прекрасные стили скобок:

foo
{
  ...
}
foo
  {
    ...
  }
foo
  while ...; do ...; done # Look, ma! No braces!
foo
( ... ) # Look, ma! No braces and a subshell to boot!

Как оболочка может сказать, что это определения функций, а не просто команда, fooза которой следуют списки команд? Во всех этих случаях необходим дополнительный устраняющий неоднозначность фактор, например ключевое слово ()или function.


С другой стороны, на самом деле это не OTBS, поскольку единственное место, где OTBS разрешает использовать фигурные скобки в новой строке, - это определения функций.
Муру

3
Я думаю, что есть аргумент, что вы были правы, чтобы охарактеризовать его как OTBS, потому что в отличие от функций в C и C ++, определение функции в сценарии оболочки в стиле Борна может быть вложено непосредственно в тело определения другой функции и, таким образом, возможно не заслуживает отличия (Или, может быть, это стиль, популярный в программировании на Java, где методы получают свою открывающую скобку в одной строке, а не в OTBS.) В любом случае, вы прекрасно понимаете, как синтаксис должен накладывать жесткие ограничения на размещение скобок для работы вообще без ()или что-то подобное.
Элия ​​Каган
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.