Qual é o uso de parênteses `()` na definição da função shell?

20

Uma função é definida como:

do_something () {
    do it
}

Eu conseguia entender o nome 'do_something' e o colchete para encapsular o código de ações, mas não consigo entender o que é o objetivo ()aqui, pois não há parâmetros nomeados nos scripts do Bash. Pode ser melhor e direto defini-lo como

do_something {
  do it
}

O que não entra em conflito com sua sintaxe atual e ainda mais declara que não há parâmetros nomeados. Para que serve ()aqui?

Álgebra
fonte
11
related: unix.stackexchange.com/questions/73750/...
Carlos Campderrós

Respostas:

44

Sem o (), a sintaxe seria realmente ambígua.

Deve haver alguma sintaxe inequívoca para definir uma função e, sem alterar substancialmente outra sintaxe do shell, não pode ser isso:

do_something {
    # one or more commands go here
}

Você disse que "não está em conflito com a sintaxe atual", mas sim! Observe que você não recebe nenhum tipo de erro de sintaxe ao tentar executar a primeira linha disso . Você recebe um erro, mas não é um erro sobre sintaxe. A segunda linha, com }, é um erro de sintaxe, mas a primeira linha não é. Em vez disso, do_something {tenta executar um comando chamado do_somethinge passar {como argumento para esse comando:

$ do_something {
do_something: command not found

Se já existe um comando chamado do_something, você está executando. Se já existe uma função chamada do_something, você está chamando . Em geral, é importante que a sintaxe seja inequívoca, mas também é importante especificamente que seja possível redefinir uma função sem chamá-la acidentalmente. Definir uma função e chamá-la não deve ser a mesma.

Como a concha trata {e (.

Como type {lhe dirá, {é uma palavra-chave shell. Isso faz com que seja [[. Se usado em uma situação em que seria um comando, {carrega semântica especial. Especificamente, ele executa o agrupamento de comandos. Em outras situações, no entanto, pode ser usado sem escape para denotar um {caractere literal . Isso inclui a situação de transmiti-lo como uma segunda palavra ou palavra subsequente de um comando.

Obviamente, o Bash poderia ter sido projetado para tratar de maneira {diferente do que atualmente. No entanto, sua sintaxe não seria mais compatível com o shell POSIX, e o Bash não seria realmente um shell no estilo Bourne e não seria capaz de executar muitos scripts de shell.

Em contraste, (é um metacaractere de concha. É sempre tratadas, especialmente se ele aparece em um comando e não é citado (com ' ', " "ou \). Portanto, não há ambiguidade na sintaxe:

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

Isso não poderia significar mais nada. Se o Bash não tivesse funções, seria um erro de sintaxe, pelo mesmo motivo que echo foo(bar)é um erro de sintaxe.

Se você realmente não gosta da ()notação, pode usar a palavra-chave functione omiti-la, como menciona sudodus . Observe que isso não faz parte da sintaxe para definir funções na maioria dos outros shells no estilo Bourne - e, em alguns, é suportado, mas as funções definidas dessa maneira têm semânticas diferentes - e, portanto, um script que o utiliza não será portátil. (O motivo pelo qual essa sintaxe pode ser inequívoca é que functionela própria é uma palavra-chave no Bash que significa que o que quer que se segue é o início de uma definição de função.)

Finalmente, observe que, embora a maioria das definições de funções seja utilizada {na prática, qualquer comando composto é permitido. Se você tivesse uma função cujo corpo você sempre quisesse executar em um subshell, poderia usar em ( )vez de { }.

Eliah Kagan
fonte
11
Em um nível superior, trata-se de expectativas e legibilidade humanas. Na maioria dos idiomas, particularmente em C, fortran e outros comuns na época em que as linguagens de script bash foram desenvolvidas, uma função / sub-rotina sempre tem uma lista de argumentos, possivelmente vazia, entre parênteses. Mude esse paradigma e você dificulta a compreensão do idioma.
Jamesqf # 11/18
15

O ()token é dizer ao interpretador de shell que você está declarando uma função.

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

Uma alternativa em bashéfunction

function do_something {
 echo 'do it'
}

ou como uma linha, você pode testar

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it
sudodus
fonte
2
A functionpalavra-chave é um ksh-ism pré-POSIX que o bash suporta para compatibilidade com versões anteriores; no entanto, o bash o suporta mal (não tornando as variáveis ​​padrão local de função, como o antigo ksh fazia nas funções declaradas nesse formulário). Conseqüentemente, não é ideal para novo código. Veja wiki.bash-hackers.org/scripting/obsolete
Charles Duffy
@CharlesDuffy, Obrigado por esta explicação :-)
sudodus
11
@CharlesDuffy Você está dizendo que o ksh e o bash aceitam alguma sintaxe que torna uma variável local no ksh, mas global no bash? Isso não parece certo. Meu entendimento, baseado em parte nessa publicação e verificação (no ksh93), é que o ksh torna variáveis ​​locais em menos situações que o bash, nunca mais. No bash, typesetsem -gem uma função sempre declara uma variável local. Com a functionpalavra - chave, variáveis ​​de escopo bash e ksh da mesma maneira , não é?
Elias Kagan 12/0418
@EliahKagan Acho que o que Charles está se referindo foi demonstrado na resposta de Gille aqui
Sergiy Kolodyazhnyy 07/01
9

Como você é muito fiel ao seu estilo !

Considere outros estilos de chaves perfeitamente finos:

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

Como o shell pode dizer que essas são definições de funções e não simplesmente um comando fooseguido por listas de comandos? Em todos esses casos, é necessário um fator de desambiguação adicional, como ()a functionpalavra-chave.

muru
fonte
Pensando bem, isso não é realmente OTBS, pois o único local em que o OTBS permite chaves em uma nova linha é para definições de funções.
Muru
3
Eu acho que há um argumento a ser argumentado de que você estava certo em caracterizá-lo como OTBS, porque, diferentemente das funções em C e C ++, uma definição de função em um shell script no estilo Bourne pode ser aninhada diretamente no corpo de outra definição de função e, portanto, sem dúvida não merece ser distinguido. (Ou talvez seja esse o estilo popular na programação Java, em que os métodos obtêm sua chave de abertura na mesma linha, em vez do OTBS.) De qualquer maneira, você faz um excelente argumento sobre como a sintaxe teria que impor restrições rígidas ao posicionamento da chave para funcionar sem ()ou algo parecido.
Elias Kagan