Разница между «функцией foo () {}» и «foo () {}»

98

Я могу определить bashфункции, используя или опуская functionключевое слово. Есть ли разница?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

Оба вызова функций fooи barуспешно, и я не вижу никакой разницы. Поэтому мне интересно, если это просто для улучшения читабельности, или есть что-то, что мне не хватает ...

Кстати, в других оболочках, таких как dash( /bin/shобозначается dashв debian / ubuntu), происходит сбой при использовании functionключевого слова.

Карлос Кампдеррос
источник
8
Кроме того, с или без скобок: function baz { echo "baz"; }. Смотрите Bashism в вики GreyCat.
manatwork

Ответы:

43

Там нет никакой разницы AFAIK, кроме того, что вторая версия является более портативной.

schaiba
источник
32
Это большая разница, переносимость ... Я вижу слишком много ответов, которые просто НЕ работают на (более старых) производственных системах, а также многие с опциями, которые работают только на Linux. По крайней мере, предупредите, что это не самый переносимый способ ... Это может быть совершенно опасно (попросить кого-то, tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " не предупредив его, сначала перепроверить, может ли версия tar на desthost избавиться от "/", может привести к бедствия в некоторых случаях ...). Например: если вы используете a function tar { #a safe tar with safety checks ... }и shигнорируете его, ...
Оливье Дюлак
1
@OlivierDulac Я хотел бы добавить, что сценарии, предполагающие shраспознавание functionключевого слова, и, в общем, предполагающие общие, но нестандартные функции, которые оболочка любит kshи bashпредлагает, также часто не будут работать в более новых производственных системах, даже если они работали в более старых выпусках та же ОС. bashдо сих пор используется shво многих системах GNU / Linux, но некоторые популярные дистрибутивы перешли на shиспользование символической ссылки на dash(оболочка Debian Almquist) для повышения производительности. Это включает в себя Debian и Ubuntu .
Элия ​​Каган
2
Без уточнения, как именно это «более портативно», ответ бесполезен.
ivan_pozdeev
В настоящее время переносимость не является аргументом, когда отрасль использует bash или эквивалентную оболочку в> 99,9% всех сред. Это сводится к удобочитаемости, и есть две стороны: некоторые люди говорят, что «функция» делает это очевидным, люди, которые изучили это из стандартов posix или других оболочек, скажут, что фигурные скобки - более очевидный стиль. В конце концов, выберите один из вашей группы или компании и придерживайтесь его.
Юбер Гжесковяк
95

functionКлючевое слово было введено в KSH . Традиционная оболочка Bourne имела только foo ()синтаксис, а POSIX стандартизирует только foo ()синтаксис.

В ATT ksh (но не в pdksh) есть несколько различий между функциями, определенными с помощью, functionи функциями, определенными с помощью синтаксиса Bourne / POSIX. В функциях , определяемых function, то typesetключевое слово объявляет локальную переменную: один раз при выходе из функции, значение переменной сбрасывается на то , что это было перед входом в функцию. С классическим синтаксисом переменные имеют глобальную область видимости независимо от того, используете вы ее typesetили нет.

$ ksh -c 'a=global; f () { typeset a=local; }; f; echo $a'
local
$ ksh -c 'a=global; function f { typeset a=local; }; f; echo $a'
global

Другое отличие в ksh состоит в том, что функции, определенные с помощью functionключевого слова, имеют свой собственный контекст прерывания. При выполнении функции ловушки, определенные вне функции, игнорируются, а фатальные ошибки внутри функции выходят только из функции, а не из всего сценария. Кроме того, $0это имя функции в функции, определенной как, functionно имя скрипта в функции, определенной с ().

Пдкш не эмулирует ATT кш. В pdksh typesetсоздает переменные локальной области видимости независимо от функции, и локальных ловушек нет (хотя использование functionимеет некоторые незначительные отличия - подробности см. На странице man).

Bash и zsh представили functionключевое слово для совместимости с ksh. Однако в этих оболочках function foo { … }и foo () { … }строго идентичны, как Баш и ЗШ расширение function foo () { … }. typesetКлючевое слово всегда объявляет локальные переменные ( за исключением -g, конечно), и ловушки не являются локальными (вы можете получить локальные ловушки в Zsh, установив local_trapsопцию).

жилль
источник
8
Следует отметить, что поддержка функции была добавлена ​​в оболочку Korn до того, как оболочка Bourne представила свой foo() commandсинтаксис, а синтаксис Bourne позже был добавлен в оболочку Korn для совместимости.
Стефан Шазелас
2
Это правильно, что function { ... }; f;пропускает fпосле functionключевого слова?
Руслан
32
foo() any-command

Синтаксис Борна поддерживается любой Борн-подобной оболочкой , но bash, yashи последние версии posh(которые поддерживают только составные команды). (оболочки Bourne и реализации AT & T kshне поддерживают, foo() any-command > redirectionsесли только any-commandэто не составная команда).

foo() any-compound-command

(примеры соединения команд: { cmd; }, for i do echo "$i"; done, (cmd)... наиболее часто используемый существо { ...; })

является синтаксисом POSIX, поддерживаемым любой подобной Bourne оболочкой, и той, которую вы обычно хотите использовать.

function foo { ...; }

синтаксис оболочки Korn, предшествующий синтаксису Борна. Используйте его, только если вы пишете специально для AT & T-реализации оболочки Korn и нуждаетесь в особой обработке, которую он там получает. Этот синтаксис не POSIX, но поддерживается bash, yashи zshдля совместимости с оболочкой Korn , хотя эти снаряды (и pdksh-А варианта Korn оболочки) не относиться к нему не отличаются от стандартного синтаксиса.

function foo () { ...; }

это синтаксис без оболочки и не должен использоваться . Это происходит только при поддержке аварии на bash, yash, zshи на pdkshоснове вариантов оболочки Korn. Кстати, это также awkсинтаксис функции.

Если мы продолжим идти по эзотерическому списку,

function foo() other-compound-command

(вроде function foo() (subshell)или function foo() for i do; ... done) еще хуже. Она поддерживается bash, yashи zsh, но не КШ, даже pdksh-На варианты.

Пока:

function foo() simple command

поддерживается только zsh.

Стефан Шазелас
источник
1
Синтаксис, включающий как functionключевое слово, так и скобки, задокументирован в Bash. В руководстве по Bash 4.2 и более поздним версиям говорится, что функции объявляются синтаксисом name () compound-command [ redirections ]или function name [()] compound-command [ redirections ]. В Bash 4.1.11 вплоть до версии 3.0-бета, которая была просто одной строкой, [ function ] name () compound-command [redirection]которая ошибочно не охватывает синтаксис, включающий functionключевое слово, но не содержит скобки, но все же охватывает синтаксис, который включает как functionключевое слово, так и скобки.
nisetama
@nise, смысл в том, что он bashраспознает function foo {в дополнение к foo() {совместимости с оболочкой Korn (и всегда имеет), чтобы он мог интерпретировать сценарии, написанные для оболочки Korn. Он также поддерживает function foo () {, но нет веских причин использовать его.
Стефан Шазелас
2
@ StéphaneChazelas Ну, я бы сказал, что есть веская причина для использования function f() {. А именно, с точки зрения читабельности, она будет распознаваться как функция всеми, кто знает английский и всеми, кто знает C, по сравнению только с одним из этих наборов.
ДепрессияДаниэль
2
Должен быть принятый ответ. Действительно важноYou should never combine the keyword function with the parentheses () when defining a function.
4wk_
Добро пожаловать в 2019 г., где существует только один отраслевой стандарт де-факто для сценариев автоматизации linux / unix; единственный интерпретатор, который установлен почти везде (за исключением экзотических сред): bash. Напишите свой код со всеми функциями bash, используйте shebang, и все будет в порядке. Аргумент совместимости недействителен.
Губерт Гжесковяк
22

Семантически эти две формы эквивалентны в Bash.

Со страницы руководства:

Функции оболочки объявляются следующим образом:

name () compound-command [redirection]
function name [()] compound-command [redirection]

Это определяет функцию с именем name. Зарезервированное слово функция не является обязательной. Если в функции указано зарезервированное слово, скобки являются необязательными.

РЕДАКТИРОВАТЬ: я только заметил, что этот вопрос помечен posix. В POSIX sh, то functionключевое слово не используется (хотя он зарезервирован).

depquid
источник
3

Несколько других уже ответили правильно, но вот мой краткий обзор:

Вторая версия является переносимой и, вероятно, будет работать со многими стандартными (особенно POSIX) оболочками.

Первая версия будет работать только с bash, но вы можете опустить скобки после имени функции вместе с ним.

В противном случае они представляют идентичные сущности после того, как bash их интерпретирует.

destenson
источник
на самом деле, первая версия не имеет смысла, кроме как ограничить ее как приемлемый синтаксис для некоторых оболочек. когда включать () иfunction ключевое слово оболочка ведет себя так , как будто вы только что сделали в foo(){ ...; }любом случае, за исключением, конечно, для оболочки , в которой он является недействительным синтаксис. и так вы должны делать, function foo { ...; }если нужно, или foo(){ ...; }иначе.
mikeserv