Unterschied zwischen "function foo () {}" und "foo () {}"

96

Ich kann bashFunktionen definieren , indem ich das functionSchlüsselwort verwende oder weglasse . Gibt es da einen Unterschied?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

Beide Aufrufe funktionieren foound sind barerfolgreich und ich sehe keinen Unterschied. Ich frage mich also, ob es nur darum geht, die Lesbarkeit zu verbessern, oder ob mir etwas fehlt ...

Übrigens schlägt dies in anderen Shells wie dash( /bin/shwird in debian dash/ ubuntu symbolisiert ) bei der Verwendung des functionSchlüsselworts fehl .

Carlos Campderrós
quelle
8
Auch mit oder ohne Klammern: function baz { echo "baz"; }. Siehe Bashism in GreyCats Wiki.
Handarbeit

Antworten:

42

Es gibt keinen Unterschied zwischen AFAIK und der Tatsache, dass die zweite Version portabler ist.

schaiba
quelle
32
Das ist ein GROSSER Unterschied, Portabilität ... Ich sehe zu viele Antworten, die einfach NICHT auf (älteren) Produktionssystemen funktionieren, und auch viele mit Optionen, die nur unter Linux funktionieren. Zumindest warnen Sie davor, dass dies nicht die üblichste Art ist ... Es könnte geradezu gefährlich sein (jemanden zu fragen, tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " ohne ihn vorher zu warnen, ob die Version von tar on desthost das "/" entfernt, was dazu führen könnte) Katastrophen in einigen Fällen ...). Zum Beispiel: Wenn Sie a verwenden function tar { #a safe tar with safety checks ... }und es shignorieren, ...
Olivier Dulac
1
@OlivierDulac Ich würde hinzufügen, dass Skripte, von denen angenommen wird, dass shsie das functionSchlüsselwort erkennen - und im Allgemeinen gemeinsame, aber nicht standardmäßige Funktionen, die Shells mögen kshund bashanbieten - auf neueren Produktionssystemen häufig auch dann nicht funktionieren , wenn sie auf älteren Releases von funktionieren das gleiche Betriebssystem. bashbietet immer noch shUnterstützung für viele GNU / Linux-Systeme, aber einige beliebte Distributionen haben auf shSymlink zu dash(der Debian Almquist SHell) umgestellt, um die Leistung zu verbessern. Dies beinhaltet Debian und Ubuntu .
Eliah Kagan
2
Ohne zu erläutern, wie genau es "portabler" ist, ist die Antwort nicht nützlich.
ivan_pozdeev
Portabilität ist heutzutage kein Argument, wenn die Branche in> 99,9% aller Umgebungen Bash oder eine gleichwertige Shell verwendet. Es kommt auf die Lesbarkeit an und es gibt zwei Seiten: Einige Leute sagen, dass "Funktion" es offensichtlich macht, Leute, die es aus den posix-Standards oder anderen Shells gelernt haben, werden sagen, dass Klammern der offensichtlichere Stil sind. Am Ende wählen Sie eine innerhalb Ihrer Gruppe oder Firma und bleiben dabei.
Hubert Grzeskowiak
93

Das functionSchlüsselwort wurde in ksh eingeführt . Die traditionelle Bourne-Shell hatte nur die foo ()Syntax, und POSIX standardisiert nur die foo ()Syntax.

In ATT ksh (aber nicht in pdksh) gibt es einige Unterschiede zwischen Funktionen, die durch functiondie Bourne / POSIX-Syntax definiert sind, und Funktionen, die mit der Bourne / POSIX-Syntax definiert sind. In Funktionen, die durch definiert sind function, typesetdeklariert das Schlüsselwort eine lokale Variable: Sobald die Funktion beendet ist, wird der Wert der Variablen auf den Wert vor dem Aufrufen der Funktion zurückgesetzt. Bei der klassischen Syntax haben Variablen einen globalen Gültigkeitsbereich, unabhängig davon, ob Sie sie verwenden typesetoder nicht.

$ 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

Ein weiterer Unterschied in ksh besteht darin, dass mit dem functionSchlüsselwort definierte Funktionen einen eigenen Trap-Kontext haben. Außerhalb der Funktion definierte Traps werden beim Ausführen der Funktion ignoriert, und schwerwiegende Fehler innerhalb der Funktion verlassen nur die Funktion und nicht das gesamte Skript. Ist auch $0der Funktionsname in einer Funktion, die durch definiert ist, functionaber der Skriptname in einer Funktion, die mit definiert ist ().

Pdksh emuliert ATT ksh nicht. typesetErstellt in pdksh unabhängig von der Funktion Variablen mit lokalem Gültigkeitsbereich und es gibt keine lokalen Traps (obwohl die Verwendung functioneinige geringfügige Unterschiede hervorruft - Einzelheiten finden Sie auf der Manpage).

Bash und zsh haben das functionSchlüsselwort für die Kompatibilität mit ksh eingeführt. Allerdings sind in diesen Shells function foo { … }und foo () { … }streng identisch, ebenso wie die Bash- und Zsh-Erweiterung function foo () { … }. Das typesetSchlüsselwort deklariert immer lokale Variablen (außer -gnatürlich) und Traps sind nicht lokal (Sie können lokale Traps in zsh erhalten, indem Sie die local_trapsOption setzen).

Gilles
quelle
8
Es sollte beachtet werden, dass die Korn-Shell vor der Einführung der Bourne-Shell um Funktionsunterstützung erweitert wurde. foo() commandDie Bourne-Syntax wurde später aus Kompatibilitätsgründen zur Korn-Shell hinzugefügt.
Stéphane Chazelas
2
Ist es richtig , dass function { ... }; f;auslässt fnach dem functionStichwort?
Ruslan
32
foo() any-command

ist die Bourne-Syntax, die von jeder Bourne-ähnlichen Shell unterstützt wird, aber bashauch von yashneueren Versionen posh(die nur zusammengesetzte Befehle unterstützen). (Die Bourne-Shell- und AT & T-Implementierungen von werden kshnur unterstützt, foo() any-command > redirectionswenn any-commandes sich um einen zusammengesetzten Befehl handelt.)

foo() any-compound-command

(Beispiele der Verbindung Befehle: { cmd; }, for i do echo "$i"; done, (cmd)... die am häufigsten verwendet wird { ...; })

ist die POSIX-Syntax, die von jeder Bourne-ähnlichen Shell unterstützt wird und die Sie normalerweise verwenden möchten.

function foo { ...; }

ist die Korn-Shell-Syntax, die der Bourne-Syntax vorausgeht. Verwenden Sie diese Option nur, wenn Sie speziell für die AT & T-Implementierung der Korn-Shell schreiben und die spezifische Behandlung benötigen, die sie dort erhält. Das Syntax ist nicht POSIX, sondern wird unterstützt durch bash, yashund zshfür die Kompatibilität mit dem Korn - Shell obwohl diese Schalen (und die pdksh-basierten Varianten des Korn - Shell) nicht behandeln es anders von der Standard - Syntax.

function foo () { ...; }

ist die Syntax ohne Shell und sollte nicht verwendet werden . Es geschieht nur durch Zufall unterstützt werden bash, yash, zshund die pdkshBasis - Varianten des Korn - Shell. Übrigens ist es auch die awkFunktionssyntax.

Wenn wir die esoterische Liste weiter durchgehen,

function foo() other-compound-command

(wie function foo() (subshell)oder function foo() for i do; ... done) ist noch schlimmer. Es wird unterstützt durch bash, yashund zsh, aber nicht ksh, auch die pdksh-basierten Varianten.

Während:

function foo() simple command

wird nur unterstützt von zsh.

Stéphane Chazelas
quelle
1
Die Syntax, die sowohl das functionSchlüsselwort als auch die Klammern enthält, ist in Bash dokumentiert. Das Handbuch von Bash 4.2 und höher besagt, dass Funktionen durch die Syntax name () compound-command [ redirections ]oder deklariert werden function name [()] compound-command [ redirections ]. In Bash 4.1.11 bis mindestens 3.0-Beta war dies nur die einzelne Zeile, [ function ] name () compound-command [redirection]die fälschlicherweise nicht die Syntax abdeckt, die das functionSchlüsselwort enthält, aber keine Klammern, sondern die Syntax, die sowohl das functionSchlüsselwort als auch die Klammern enthält.
Nisetama
@nise, der Punkt ist, dass es zusätzlich zur Kompatibilität mit der Korn-Shell basherkennt (und immer erkennt), so dass es Skripte interpretieren kann, die für die Korn-Shell geschrieben wurden. Es unterstützt auch, aber es gibt keinen guten Grund, diesen zu verwenden. function foo {foo() {function foo () {
Stéphane Chazelas
2
@ StéphaneChazelas Naja, ich würde sagen, es gibt einen guten Grund dafür function f() {. In Bezug auf die Lesbarkeit wird es nämlich von jedem, der Englisch kann, und von jedem, der C kann, als Funktion gegenüber nur einer dieser Mengen erkannt.
DepressedDaniel
2
Sollte die akzeptierte Antwort sein. Wirklich wichtigYou should never combine the keyword function with the parentheses () when defining a function.
4wk_
Willkommen im Jahr 2019, wo es nur einen Industriestandard für Linux / Unix-Automatisierungsskripte gibt. der einzige Interpreter, der fast überall installiert ist (außer in exotischen Umgebungen): bash. Schreiben Sie Ihren Code mit allen Bash-Funktionen, verwenden Sie den Shebang, und alles wird gut. Das Argument der Vereinbarkeit ist nichtig.
Hubert Grzeskowiak
22

Semantisch sind diese beiden Formen in Bash äquivalent.

Von der Manpage:

Shell-Funktionen werden wie folgt deklariert:

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

Dies definiert eine Funktion mit dem Namen name. Die reservierte Wort Funktion ist optional. Wenn das für die Funktion reservierte Wort angegeben wird, sind die Klammern optional.

EDIT: Ich habe gerade bemerkt, dass diese Frage markiert ist posix. In POSIX shwird das functionSchlüsselwort nicht verwendet (obwohl es reserviert ist).

depquid
quelle
3

Einige andere haben inzwischen richtig geantwortet, aber hier ist meine kurze Zusammenfassung:

Die zweite Version ist portabel und funktioniert wahrscheinlich mit vielen Standard-Shells (insbesondere POSIX).

Die erste Version funktioniert nur mit Bash. Sie können jedoch die Klammern nach dem Funktionsnamen weglassen.

Andernfalls stellen sie identische Entitäten dar, nachdem die Bash sie interpretiert hat.

Destenson
quelle
Tatsächlich macht die erste Version keinen Sinn, außer sie als akzeptable Syntax auf einige Shells zu beschränken. Wenn Sie das Schlüsselwort () undfunction einfügen, verhält sich die Shell so, als ob Sie es foo(){ ...; }trotzdem getan hätten , außer natürlich für eine Shell, in der es sich um eine ungültige Syntax handelt. und so solltest du tun, function foo { ...; }wenn du musst, oder foo(){ ...; }anders.
mikeserv