diferença entre "função foo () {}" e "foo () {}"

97

Eu posso definir bashfunções usando ou omitindo a functionpalavra - chave. Existe alguma diferença?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

Ambas chamam para funções fooe são barbem - sucedidas e não vejo diferença. Então, eu estou querendo saber se é apenas para melhorar a legibilidade, ou há algo que estou perdendo ...

BTW em outros shells como dash( /bin/shestá simbolizado dashno debian / ubuntu) falha ao usar a functionpalavra - chave.

Carlos Campderrós
fonte
8
Também com ou sem o parêntese: function baz { echo "baz"; }. Veja Bashism no wiki de GreyCat.
manatwork

Respostas:

42

Não há diferença AFAIK, exceto o fato de que a segunda versão é mais portátil.

Schaiba
fonte
32
Essa é uma GRANDE diferença, portabilidade ... Vejo muitas respostas que simplesmente NÃO funcionam em sistemas de produção (mais antigos), e também muitas com opções que funcionam apenas no Linux. Pelo menos, avise que essa não é a maneira mais portátil ... Isso pode ser totalmente perigoso (pedir a alguém tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " sem avisar que verifique primeiro se a versão do tar no desthost se livrará do "/" poderia levar a desastres em alguns casos ...). Por ex: se o uso de um function tar { #a safe tar with safety checks ... }e shignora-lo, ...
Olivier Dulac
1
@OlivierDulac Gostaria de acrescentar que os scripts que assumem shreconhecer a functionpalavra-chave - e, em geral, que assumem recursos comuns, mas não padronizados, que os shells gostam kshe bashoferecem - também geralmente não funcionam em sistemas de produção mais novos, mesmo se eles trabalharem em versões mais antigas do o mesmo sistema operacional. bashainda fornece shmuitos sistemas GNU / Linux, mas algumas distribuições populares passaram a ter shum link simbólico para dash(o Debian Almquist SHell) para melhorar o desempenho. Isso inclui o Debian e o Ubuntu .
Elias Kagan
2
Sem detalhar como exatamente é "mais portátil", a resposta não é útil.
ivan_pozdeev 22/03
Hoje em dia, a portabilidade não é um argumento em que o setor está usando o bash ou um shell equivalente em> 99,9% de todos os ambientes. Tudo se resume à legibilidade e há dois lados: algumas pessoas dizem que "função" torna óbvio, as pessoas que aprenderam com os padrões posix ou outras conchas dizem que aparelhos são o estilo mais óbvio. No final, escolha um dentro do seu grupo ou empresa e cumpra-o.
Hubert Grzeskowiak
94

A functionpalavra-chave foi introduzida no ksh . O shell Bourne tradicional tinha apenas a foo ()sintaxe e o POSIX padroniza apenas a foo ()sintaxe.

No ATT ksh (mas não no pdksh), existem algumas diferenças entre funções definidas por functione funções definidas com a sintaxe Bourne / POSIX. Nas funções definidas por function, a typesetpalavra - chave declara uma variável local: uma vez que a função é encerrada, o valor da variável é redefinido para o que era antes de entrar na função. Com a sintaxe clássica, as variáveis ​​têm um escopo global, independentemente de você usar typesetou não.

$ 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

Outra diferença no ksh é que as funções definidas com a functionpalavra - chave têm seu próprio contexto de interceptação. Os desvios definidos fora da função são ignorados durante a execução da função e os erros fatais dentro da função saem apenas da função e não de todo o script. Além disso, $0é o nome da função em uma função definida por, functionmas o nome do script em uma função definida com ().

O Pdksh não emula o ATT ksh. No pdksh, typesetcria variáveis ​​com escopo local, independentemente da função, e não há traps locais (embora o uso functionfaça algumas pequenas diferenças - consulte a página de manual para obter detalhes).

Bash e zsh introduziram a functionpalavra - chave para compatibilidade com o ksh. No entanto, nessas conchas function foo { … }e foo () { … }são estritamente idênticas, como é a extensão bash e zsh function foo () { … }. A typesetpalavra-chave sempre declara variáveis ​​locais (exceto -gnaturalmente) e os traps não são locais (você pode obter traps locais no zsh configurando a local_trapsopção).

Gilles
fonte
8
Deve-se notar que o suporte à função foi adicionado ao shell Korn antes que o shell Bourne introduzisse sua foo() commandsintaxe, e a sintaxe Bourne foi adicionada posteriormente ao shell Korn para compatibilidade.
Stéphane Chazelas 29/04
2
É correto que function { ... }; f;omita fapós a functionpalavra - chave?
Ruslan
32
foo() any-command

é a sintaxe Bourne suportada por qualquer shell semelhante ao Bourne bash, yashe versões recentes posh(que suportam apenas comandos compostos). (as implementações Bourne shell e AT&T de kshnão suportam, a foo() any-command > redirectionsmenos que any-commandseja um comando composto).

foo() any-compound-command

(exemplos de composto de comandos: { cmd; }, for i do echo "$i"; done, (cmd)... a ser mais utilizada { ...; })

é a sintaxe do POSIX suportada por qualquer shell semelhante ao Bourne e a que você geralmente deseja usar.

function foo { ...; }

é a sintaxe do shell Korn, que antecede a sintaxe Bourne. Use este apenas se estiver escrevendo especificamente para a implementação da shell Korn da AT&T e precisar do tratamento específico recebido nele. Essa sintaxe não é POSIX, mas é suportada por bash, yashe zshpara compatibilidade com o shell Korn, embora esses shells (e as pdkshvariantes baseadas no shell Korn) não o tratem de forma diferente da sintaxe padrão.

function foo () { ...; }

é a sintaxe de nenhum shell e não deve ser usada . Isso só acontece de ser apoiado por acidente por bash, yash, zshe as pdkshvariantes com base do shell Korn. Aliás, também é a awksintaxe da função.

Se continuarmos descendo a lista esotérica,

function foo() other-compound-command

(como function foo() (subshell)ou function foo() for i do; ... done) é ainda pior. É suportado por bash, yashe zsh, mas não por ksh, até pelas pdkshvariantes baseadas em.

Enquanto:

function foo() simple command

é suportado apenas por zsh.

Stéphane Chazelas
fonte
1
A sintaxe que inclui a functionpalavra - chave e os parênteses está documentada no Bash. O manual do Bash 4.2 e posterior diz que as funções são declaradas pela sintaxe name () compound-command [ redirections ]ou function name [()] compound-command [ redirections ]. No Bash 4.1.11, até pelo menos 3.0-beta, que era apenas a única linha [ function ] name () compound-command [redirection]que, erroneamente, não cobre a sintaxe que inclui a functionpalavra - chave, mas não os parênteses, mas ainda cobre a sintaxe que inclui a functionpalavra - chave e os parênteses.
Nisetama 18/06/2016
@nise, o ponto é que bashreconhece function foo {, além foo() {de compatibilidade com o shell Korn (e sempre tem) para que ele possa interpretar scripts escritos para o shell Korn. Ele também suporta function foo () {, mas não há boas razões para usá-lo.
Stéphane Chazelas
2
@ StéphaneChazelas Bem, eu diria que há uma boa razão para usar function f() {. Ou seja, em termos de legibilidade, será reconhecido como uma função por qualquer pessoa que saiba inglês e qualquer pessoa que conheça C, em comparação a apenas um desses conjuntos.
DepressedDaniel
2
Deve ser a resposta aceita. Realmente importanteYou should never combine the keyword function with the parentheses () when defining a function.
4wk_
Bem-vindo a 2019, onde existe apenas um padrão de fato do setor para scripts de automação linux / unix; o único intérprete que está instalado praticamente em qualquer lugar (ambientes exóticos à parte): bash. Escreva seu código com todos os recursos do bash, use o shebang e você ficará bem. O argumento da compatibilidade é nulo.
Hubert Grzeskowiak
22

Semanticamente, essas duas formas são equivalentes no Bash.

Na página do manual:

As funções do shell são declaradas da seguinte maneira:

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

Isso define uma função denominada name. A função de palavra reservada é opcional. Se a palavra reservada da função for fornecida, os parênteses são opcionais.

EDIT: Acabei de perceber que esta pergunta está marcada posix. No POSIX sh, a functionpalavra-chave não é usada (embora seja reservada).

depquid
fonte
3

Vários outros já responderam corretamente até agora, mas aqui está minha sinopse concisa:

A segunda versão é portátil e provavelmente funcionará com muitos shells padrão (particularmente POSIX).

A primeira versão funcionará apenas com o bash, mas você pode omitir os parênteses após o nome da função.

Caso contrário, eles representam entidades idênticas depois que o bash as interpreta.

destenson
fonte
na verdade, a primeira versão não faz sentido, exceto limitá-la como sintaxe aceitável a alguns shells. quando você incluir o () e a functionpalavra-chave do shell se comporta como se você acabou de fazer foo(){ ...; }de qualquer maneira, exceto, é claro, por uma concha em que é sintaxe inválida. e assim você deve fazer function foo { ...; }se for necessário ou foo(){ ...; }não.
precisa saber é o seguinte