2015-01-06 5 views
21

Обычно функция Баша определяется с помощью фигурных скобок, чтобы заключить тело:Баш функции: Вшито тело в фигурных скобках против скобки

foo() 
{ 
    ... 
} 

При работе над сценарием оболочки сегодня широко использует функцию, I» мы сталкиваемся с проблемами с переменными, которые имеют одно и то же имя в вызываемом как в вызывающей функции, а именно, что эти переменные одинаковы. Я узнал, что это можно предотвратить, указав локальные переменные внутри функции как локальные: local var=xyz.

Затем, в какой-то момент, я обнаружил нить (Defining bash function body using parenthesis instead of braces), в котором он объяснил, что это так же, как действует определить функцию с помощью круглых скобок, как это:

foo() 
(
    ... 
) 

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

  • Почему фигурные скобки, используемые по умолчанию, чтобы приложить тело функции вместо скобок ?

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

  • Существуют ли другие основные недостатки (*) для использования круглых скобок вместо фигурных скобок (что может объяснить, почему брекеты, кажется, предпочтительнее)?

(*) Я знаю (из исключений, связанных с обсуждения, я наткнулся в течение долгого времени), что некоторые утверждают, что в явном виде, используя статус ошибки гораздо лучше, чем быть в состоянии выйти из любой точки мира, но я предпочитаю последнее.

Очевидно, что оба стиля имеют свои преимущества и недостатки. Так что я надеюсь, что некоторые из вас более опытные пользователи Баш могут дать мне некоторые общие рекомендации:

  • Когда я должен использовать фигурные скобки, чтобы приложить тело функции, и, когда это целесообразно, чтобы переключиться в круглые скобки?

EDIT: Take Визитки из ответов

Спасибо за ваши ответы, моя голова теперь немного яснее в отношении к этому. Так что я забираю из ответов:

  • Придерживайтесь обычных фигурные скобки, если только для того, чтобы не запутать потенциальных других пользователей/разработчиков сценария (и даже использовать фигурные скобки, если все тело завернутый в круглые скобки).

  • Единственный реальный недостаток фигурных скобок заключается в том, что любая переменная в родительской области может быть изменена, хотя в некоторых ситуациях это может быть преимуществом. Это можно легко обойти, объявив переменные как local.

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

+0

Ваше редактирование - очень хорошая упаковка! – fedorqui

ответ

12

Почему фигурные скобки, используемые по умолчанию, чтобы приложить тело функции вместо скобок?

Тело функции может быть любой составной командой. Обычно это { list; }, но технически допустимы три другие формы составных команд: (list), ((expression)) и [[ expression ]].

C и языки в C-семействе, такие как C++, Java, C# и JavaScript, все используют фигурные скобки для разграничения тел функций.Кудрявые скобки являются наиболее естественным синтаксисом для программистов, знакомых с этими языками.

Существуют ли другие основные недостатки (*) для использования скобок вместо скобок (что может объяснить, почему скобки предпочтительнее)?

Да. Есть много вещей, которые вы не можете сделать из подколонки, в том числе:

  • Изменение глобальных переменных. Изменения переменных не распространяются на родительскую оболочку.
  • Выход из сценария. Оператор exit выйдет только из суб-оболочки.

Запуск суб-оболочки также может стать серьезным ударом. Каждый раз, когда вы вызываете эту функцию, вы запускаете новый процесс.

Вы также можете получить странное поведение, если ваш скрипт убит. Сигналы, получаемые родительской и дочерней оболочками, будут изменены. Это тонкий эффект, но если у вас есть обработчики trap или у вас kill ваш скрипт, эти части не работают так, как вы хотите.

Когда я должен использовать фигурные скобки, чтобы заключить тело функции, и когда целесообразно переключиться на круглые скобки?

Я бы посоветовал всегда использовать фигурные скобки. Если вы хотите явную под-оболочку, добавьте набор скобок внутри фигурных скобок. Использование только скобок - очень необычный синтаксис и смущает многих людей, читающих ваш скрипт.

foo() { 
    (
     subshell commands; 
    ) 
} 
+0

Хотя я впервые рассмотрел @ kojiro, я приму ваш ответ, так как вы явно дали ответьте на все три вопроса, плюс вы подняли два очень хороших момента с поведением плотины, если сценарий убит (не подумал бы об этом), и что использование круглых скобок без каких-либо фигурных скобок было бы слишком необычным синтаксисом. – flotzilla

+0

Но я по-прежнему вижу возможность изменять глобальные переменные из коробки, но должен явно объявлять локальные переменные, такие как более опасная, чем плюс. Я бы предпочел, чтобы переменные были локальными по умолчанию, но были способны объявить их глобальными, чтобы их можно было получить. Но я просто привык бы всегда объявлять их «местными», чтобы обойти это. – flotzilla

+1

@VaticanViolator Я согласен, было бы лучше, если бы они были локальными по умолчанию. Это всего лишь одна из тех вещей, которые вы научитесь принимать и решать. Например, я называю локальные переменные в строчных и глобальных переменных в верхнем регистре. Он дает понять, что является и что снижает риск случайного попирания глобальных переменных, если я забуду «локальную» декларацию. –

4

Это действительно имеет значение. Поскольку функции bash не возвращают значения, а используемые переменные из глобальной области (то есть они могут обращаться к переменным из «вне» своей области), обычный способ обработки вывода функции - хранить значение в переменную, а затем вызвать ее.

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

Смотрите пример:

$ cat a.sh 
#!/bin/bash 

func_braces() { #function with curly braces 
echo "in $FUNCNAME. the value of v=$v" 
v=4 
} 

func_parentheses() (
echo "in $FUNCNAME. the value of v=$v" 
v=8 
) 


v=1 
echo "v=$v. Let's start" 
func_braces 
echo "Value after func_braces is: v=$v" 
func_parentheses 
echo "Value after func_parentheses is: v=$v" 

Давайте выполним его:

$ ./a.sh 
v=1. Let's start 
in func_braces. the value of v=1 
Value after func_braces is: v=4 
in func_parentheses. the value of v=4 
Value after func_parentheses is: v=4 # the value did not change in the main shell 
+1

технически функции могут возвращать значения кода выхода через 'return', который будет возвращаться к родительскому объекту, доступному через цепочку или' $? ' – Catskul

4

Я предпочитаю использовать подоболочку, когда я хочу, чтобы изменить каталоги, но всегда с одной и той же исходной директории, и не может быть надоело использовать pushd/popd или самостоятельно управлять каталогами.

for d in */; do 
    (cd "$d" && dosomething) 
done 

Это будет работать, а из тела функции, но даже если вы определить функции с фигурными скобками, это все еще можно использовать его с субоболочки.

doit() { 
    cd "$1" && dosomething 
} 
for d in */; do 
    (doit "$d") 
done 

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

myfun() { 
    local x=123 
} 

Так что я бы сказал, явно определить свою функцию субоболочки только если не является подоболочкой, что вредно для очевидного правильного поведения этой функции.

Общая информация: В качестве примечания стороны считайте, что bash на самом деле всегда рассматривает функцию как формулу фигурной фигурной скобки. Это просто иногда имеет круглые скобки в нем:

$ f() (echo hi) 
$ type f 
f is a function 
f() 
{ 
    (echo hi) 
}