différence entre «fonction foo () {}» et «foo () {}»

98

Je peux définir des bashfonctions en utilisant ou en omettant le functionmot clé. Y a-t-il une différence?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

Les deux appels aux fonctions fooet barréussissent et je ne vois aucune différence. Alors je me demande si c'est juste pour améliorer la lisibilité, ou s'il y a quelque chose qui me manque ...

BTW dans les autres shells comme dash( /bin/shest lié à un lien symbolique dashdans debian / ubuntu), il échoue lors de l’utilisation du functionmot clé.

Carlos Campderrós
la source
8
De plus , avec ou sans la parenthèse: function baz { echo "baz"; }. Voir Bashism dans le wiki de GreyCat.
manatwork

Réponses:

43

Autant que je sache, à part le fait que la deuxième version est plus portable.

schaiba
la source
32
C’est une grosse différence, la portabilité ... Je vois trop de réponses qui ne fonctionnent tout simplement PAS sur les systèmes de production (les plus anciens), et beaucoup d’options qui ne fonctionnent que sur Linux. Au moins, avertissez-le que ce n'est pas le moyen le plus portable ... Cela pourrait être carrément dangereux (demander à quelqu'un de le tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " prévenir sans vérifier au préalable de vérifier si la version de tar sur desthost se débarrassera du "/" pourrait conduire à catastrophes dans certains cas ...). Par exemple, si on l'utilise function tar { #a safe tar with safety checks ... }et qu'on l' shignore, ...
Olivier Dulac
1
@OlivierDulac J'ajouterais que les scripts qui supposent shle functionmot-clé - et, en général, qui supposent des fonctionnalités communes mais non standard qui ressemblent à une coquille kshet bashoffrent - ne fonctionneront souvent pas sur les systèmes de production les plus récents , même s'ils fonctionnaient sur des versions plus anciennes de le même OS. bashfournit encore shsur de nombreux systèmes GNU / Linux, mais certaines distributions populaires ont opté pour shun lien symbolique vers dash(la Debian Almquist SHell) afin d’améliorer les performances. Cela inclut Debian et Ubuntu .
Eliah Kagan
2
Sans préciser dans quelle mesure il est "plus portable", la réponse n’est pas utile.
ivan_pozdeev
La portabilité n’est plus un argument de nos jours où l’industrie utilise bash ou un shell équivalent dans> 99,9% des environnements. Cela se résume à de la lisibilité et il y a deux côtés: certaines personnes disent que "fonction" le rend évident, ceux qui l'ont appris grâce aux standards posix ou à d'autres coquilles diront que les accolades sont le style le plus évident. En fin de compte, choisissez-en un au sein de votre groupe ou de votre entreprise et respectez-le.
Hubert Grzeskowiak
95

Le functionmot clé a été introduit en ksh . Le shell Bourne traditionnel n’avait que la foo ()syntaxe et POSIX ne standardise que la foo ()syntaxe.

Dans ATT ksh (mais pas pdksh), il existe quelques différences entre les fonctions définies par functionet les fonctions définies avec la syntaxe Bourne / POSIX. Dans les fonctions définies par function, le typesetmot - clé déclare une variable locale: une fois la fonction fermée, la valeur de la variable est réinitialisée à ce qu'elle était avant d'entrer dans la fonction. Avec la syntaxe classique, les variables ont une portée globale, que vous utilisiez typesetou non.

$ 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

Une autre différence dans ksh est que les fonctions définies avec le functionmot clé ont leur propre contexte d'interruption. Les interruptions définies en dehors de la fonction sont ignorées lors de l'exécution de la fonction et les erreurs fatales à l'intérieur de la fonction sortent uniquement de la fonction et non du script entier. Aussi, $0est le nom de la fonction dans une fonction définie par functionmais le nom du script dans une fonction définie avec ().

Pdksh n'émule pas ATT ksh. Dans pdksh, typesetcrée des variables de portée locale, quelle que soit la fonction, et il n'y a pas de pièges locaux (bien que l'utilisation functionapporte quelques différences mineures - voir la page de manuel pour plus de détails).

Bash et zsh ont introduit le functionmot - clé pour la compatibilité avec ksh. Cependant, dans ces shells function foo { … }et foo () { … }sont strictement identiques, de même que les extensions bash et zsh function foo () { … }. Le typesetmot clé déclare toujours les variables locales (sauf avec -gbien sûr), et les traps ne sont pas locaux (vous pouvez obtenir des traps locaux dans zsh en définissant l' local_trapsoption).

Gilles
la source
8
Il convient de noter que la prise en charge des fonctions a été ajoutée au shell Korn avant que le shell Bourne n'introduise sa foo() commandsyntaxe et que celle-ci a ensuite été ajoutée au shell Korn pour des raisons de compatibilité.
Stéphane Chazelas
2
Est-il correct d' function { ... }; f;omettre faprès le functionmot clé?
Ruslan
32
foo() any-command

est la syntaxe Bourne supportée par tout shell de type Bourne mais bash, yashet les versions récentes de posh(qui ne supportent que les commandes composées). (Les implémentations de Bourne Shell et d’AT & T kshne prennent pas en charge foo() any-command > redirectionssauf s’il any-commands’agit d’une commande composée).

foo() any-compound-command

(exemples de commandes composé: { cmd; }, for i do echo "$i"; done, (cmd)... l'être le plus couramment utilisé { ...; })

est la syntaxe POSIX prise en charge par tout shell de type Bourne et celui que vous souhaitez généralement utiliser.

function foo { ...; }

est la syntaxe du shell Korn, antérieure à la syntaxe Bourne. Utilisez uniquement celui-ci si vous écrivez spécifiquement pour la mise en œuvre de la coque Korn chez AT & T et si vous avez besoin du traitement spécifique qui y est appliqué. Cette syntaxe n'est pas POSIX, mais est pris en charge par bash, yashet zshpour la compatibilité avec le shell Korn si ces coquilles (et les pdkshvariantes à base de Korn shell) ne traite pas différent de la syntaxe standard.

function foo () { ...; }

est la syntaxe de no shell et ne doit pas être utilisé . Il arrive que d'être pris en charge par accident par bash, yash, zshet les pdkshvariantes à base de Korn shell. Incidemment, c'est aussi la awksyntaxe de la fonction.

Si nous continuons à descendre la liste ésotérique,

function foo() other-compound-command

(comme function foo() (subshell)ou function foo() for i do; ... done) est encore pire. Il est supporté par bash, yashet zshnon pas ksh, même les pdkshvariantes basées sur.

Tandis que:

function foo() simple command

est uniquement supporté par zsh.

Stéphane Chazelas
la source
1
La syntaxe qui inclut à la fois le functionmot clé et les parenthèses est documentée dans Bash. Le manuel de Bash 4.2 et versions ultérieures indique que les fonctions sont déclarées par la syntaxe name () compound-command [ redirections ]ou function name [()] compound-command [ redirections ]. Dans Bash 4.1.11, au moins la version 3.0-bêta était une simple ligne [ function ] name () compound-command [redirection]qui ne couvrait pas, par erreur, la syntaxe incluant le functionmot clé, mais pas les parenthèses, mais couvrant néanmoins la syntaxe comprenant le functionmot clé et les parenthèses.
Nisetama
@nise, le point est que bashreconnaît , function foo {en plus de foo() {la compatibilité avec le shell Korn (et a toujours) de sorte qu'il peut interpréter des scripts écrits pour le shell Korn. Il prend également en charge function foo () {, mais il n'y a aucune bonne raison de l'utiliser.
Stéphane Chazelas
2
@ StéphaneChazelas Eh bien, je dirais qu'il y a une bonne raison d'utiliser function f() {. En termes de lisibilité, cette fonction sera reconnue par tous ceux qui connaissent l'anglais et ceux qui connaissent le C, par opposition à un seul de ces ensembles.
DepressedDaniel
2
Devrait être la réponse acceptée. Vraiment importantYou should never combine the keyword function with the parentheses () when defining a function.
4wk_
Bienvenue en 2019, où il n'existe qu'un seul standard de l'industrie pour les scripts d'automatisation Linux / Unix. le seul interprète qui s’est installé un peu partout (abstraction faite des environnements exotiques): bash. Ecrivez votre code avec toutes les fonctionnalités bash, utilisez le shebang, et tout ira bien. L'argument de la compatibilité est nul.
Hubert Grzeskowiak
22

Sémantiquement, ces deux formes sont équivalentes dans Bash.

De la page de manuel:

Les fonctions du shell sont déclarées comme suit:

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

Ceci définit une fonction nommée name. La fonction mot réservé est facultative. Si le mot réservé de la fonction est fourni, les parenthèses sont facultatives.

EDIT: Je viens de remarquer que cette question est étiquetée posix. Dans POSIX sh, le functionmot-clé n'est pas utilisé (bien qu'il soit réservé).

mauve
la source
3

Plusieurs autres ont déjà répondu correctement, mais voici mon résumé concis:

La deuxième version est portable et est susceptible de fonctionner avec de nombreux shells standard (en particulier POSIX).

La première version fonctionnera uniquement avec bash, mais vous pouvez omettre les parenthèses après le nom de la fonction.

Sinon, ils représentent des entités identiques après leur interprétation par bash.

Destenson
la source
en fait, la première version n’a aucun sens si ce n’est de la limiter comme syntaxe acceptable à certains shells. Lorsque vous incluez le () et le functionmot - clé, le shell se comporte comme si vous foo(){ ...; }veniez de le faire, sauf, bien sûr, pour un shell dans lequel la syntaxe n'est pas valide. et donc vous devriez le faire function foo { ...; }si vous devez, ou foo(){ ...; }autrement.
mikeserv