Quelle est l'utilité des parenthèses `()` dans la définition de la fonction shell?

20

Une fonction est définie comme:

do_something () {
    do it
}

Je pouvais comprendre son nom 'do_something' et la parenthèse bouclée pour encapsuler le code d'actions, mais je ne suis pas en mesure de comprendre à quoi sert ()ici, car il n'y a pas de paramètres nommés dans les scripts Bash. Il pourrait être préférable et simple de le définir comme

do_something {
  do it
}

Ce qui n'entre pas en conflit avec sa syntaxe actuelle, et encore plus déclare qu'il n'y a pas de paramètres nommés. Quelle est l'utilité ()ici?

Algèbre
la source
1
en relation: unix.stackexchange.com/questions/73750/…
Carlos Campderrós

Réponses:

44

Sans le (), la syntaxe serait vraiment ambiguë.

Il doit y avoir une syntaxe sans ambiguïté pour définir une fonction, et sans modifier substantiellement l' autre syntaxe du shell, cela ne peut pas être ceci:

do_something {
    # one or more commands go here
}

Vous avez dit que cela "ne se confond pas avec sa syntaxe actuelle", mais c'est le cas! Notez que vous n'obtenez aucune sorte d'erreur de syntaxe lorsque vous essayez d'exécuter la première ligne de cela . Vous obtenez une erreur, mais ce n'est pas une erreur de syntaxe. La deuxième ligne, avec }, est une erreur de syntaxe, mais pas la première ligne. Au lieu de cela, do_something {tente d'exécuter une commande appelée do_somethinget de passer {comme argument à cette commande:

$ do_something {
do_something: command not found

S'il existe déjà une commande appelée do_something, vous l'exécutez. S'il existe déjà une fonction appelée do_something, vous l'appelez . Il est important en général que la syntaxe soit sans ambiguïté, mais il est également important spécifiquement qu'il soit possible de redéfinir une fonction sans l'appeler accidentellement à la place. Définir une fonction et l'appeler ne doit pas se ressembler.

Comment la coquille traite {et (.

Comme type {vous le direz, {est un mot clé shell. Cela fait comme [[. S'il est utilisé dans une situation où il s'agirait autrement d'une commande, il {porte une sémantique spéciale. Plus précisément, il effectue un regroupement de commandes. Dans d'autres situations, cependant, il peut être utilisé sans échappatoire pour désigner un {caractère littéral . Cela inclut la situation de le passer comme un deuxième mot ou un mot suivant d'une commande.

Bien sûr, Bash aurait pu être conçu pour traiter {différemment de ce qu'il fait actuellement. Cependant, sa syntaxe n'aurait alors plus été compatible avec le shell POSIX, et Bash ne serait pas vraiment un shell de style Bourne et ne serait pas capable d'exécuter de nombreux scripts shell.

En revanche, (est un métacaractère shell. Il est toujours traité spécialement si elle apparaît dans une commande et ne sont pas cotés (avec ' ', " "ou \). Il n'y a donc aucune ambiguïté dans la syntaxe:

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

Cela ne pouvait pas signifier autre chose. Si Bash n'avait pas de fonctions, ce serait une erreur de syntaxe, pour la même raison echo foo(bar)c'est une erreur de syntaxe.

Si vous n'aimez pas vraiment la ()notation, vous pouvez utiliser le mot-clé functionet l'omettre, comme le mentionne sudodus . Notez que cela ne fait pas partie de la syntaxe de définition des fonctions dans la plupart des autres shells de style Bourne - et dans certains cas, il est pris en charge mais les fonctions définies de cette façon ont une sémantique différente - et donc un script qui l'utilise ne sera pas portable. (La raison pour laquelle cette syntaxe peut être sans ambiguïté est qu'elle functionest elle-même un mot clé dans Bash qui signifie que tout ce qui suit est le début d'une définition de fonction.)

Enfin, notez que bien que la plupart des définitions de fonctions utilisent {en pratique, toute commande composée est autorisée. Si vous aviez une fonction dont vous vouliez toujours exécuter le corps en sous-shell, vous pourriez utiliser ( )plutôt que { }.

Eliah Kagan
la source
1
À un niveau supérieur, il s'agit des attentes humaines et de la lisibilité. Dans la plupart des langages, en particulier en C, fortran et d'autres courants au moment où les langages de script bash ont été développés, une fonction / sous-routine a toujours une liste d'arguments, éventuellement vide, qui est placée entre parenthèses. Changez ce paradigme et vous compliquez la compréhension de la langue.
jamesqf
15

Le ()jeton est de dire à l'interpréteur de shell que vous déclarez une fonction.

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

Une alternative bashestfunction

function do_something {
 echo 'do it'
}

ou en une ligne, vous pouvez tester

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it
sudodus
la source
2
Le functionmot-clé est un ksh-ism pré-POSIX que bash prend en charge pour une compatibilité descendante; cependant, bash le supporte mal (ne faisant pas des variables fonction-local par défaut, comme le faisait l'ancien ksh dans les fonctions déclarées sous cette forme). Par conséquent, ce n'est pas idéal pour un nouveau code. Voir wiki.bash-hackers.org/scripting/obsolete
Charles Duffy
@CharlesDuffy, Merci pour cette explication :-)
sudodus
1
@CharlesDuffy Êtes-vous en train de dire que ksh et bash acceptent une syntaxe qui rend une variable locale dans ksh mais globale dans bash? Cela ne semble pas juste. Ma compréhension, basée en partie sur ce post et la vérification (dans ksh93), est que ksh rend les variables locales dans moins de situations que bash, jamais plus. En bash, typesetsans -gdans une fonction déclare toujours une variable locale. Avec les functionvariables de portée mot - clé, bash et ksh de la même manière , n'est-ce pas?
Eliah Kagan
@EliahKagan Je pense que ce à quoi Charles faisait référence a été démontré dans la réponse de Gille ici
Sergiy Kolodyazhnyy
9

Comme vous êtes vraiment une vraie accolade !

Considérez d'autres styles d'accolade parfaitement fins:

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

Comment le shell peut-il dire qu'il s'agit de définitions de fonctions et pas simplement d'une commande foosuivie de listes de commandes? Dans tous ces cas, un facteur d'ambiguïté supplémentaire, comme ()ou le functionmot-clé est nécessaire.

muru
la source
À la réflexion, ce n'est pas réellement OTBS, car le seul endroit où OTBS autorise les accolades sur une nouvelle ligne est pour les définitions de fonction.
muru
3
Je pense qu'il y a un argument à faire valoir que vous aviez raison de le qualifier d'OTBS, car contrairement aux fonctions en C et C ++, une définition de fonction dans un script shell de style Bourne peut être imbriquée directement dans le corps d'une autre définition de fonction et donc sans doute ne mérite pas d'être distingué. (Ou peut-être que c'est le style populaire en programmation Java où les méthodes obtiennent leur accolade ouvrante sur la même ligne, plutôt que OTBS.) du tout sans ()ou quelque chose comme ça.
Eliah Kagan