Was ist die Verwendung von Klammern `()` in der Definition von Shell-Funktionen?

20

Eine Funktion ist definiert als:

do_something () {
    do it
}

Ich konnte den Namen 'do_something' und die geschweifte Klammer verstehen, um den Aktionscode zu kapseln, aber ich kann nicht verstehen, was der Zweck ()hier ist, da es in Bash-Skripten keine benannten Parameter gibt. Es könnte besser und einfacher sein, es als zu definieren

do_something {
  do it
}

Das widerspricht nicht der aktuellen Syntax und erklärt sogar, dass es keine benannten Parameter gibt. Was ist die Verwendung von ()hier?

Algebra
quelle
1
Related: unix.stackexchange.com/questions/73750/…
Carlos Campderrós

Antworten:

44

Ohne das ()wäre die Syntax wirklich mehrdeutig.

Es muss sein , eine eindeutige Syntax zum Definieren einer Funktion und ohne wesentliche Änderung anderer Shell - Syntax, ist es dies nicht sein kann:

do_something {
    # one or more commands go here
}

Sie sagten, dies "widerspricht nicht der aktuellen Syntax", aber es tut! Beachten Sie, dass Sie keinen Syntaxfehler erhalten, wenn Sie versuchen, die erste Zeile davon auszuführen . Sie erhalten einen Fehler, aber es handelt sich nicht um einen Syntaxfehler. Die zweite Zeile mit }ist ein Syntaxfehler, die erste Zeile jedoch nicht. do_something {Versuchen Sie stattdessen, einen aufgerufenen Befehl auszuführen do_somethingund {als Argument an diesen Befehl zu übergeben:

$ do_something {
do_something: command not found

Wenn bereits ein Befehl aufgerufen wurde do_something, führen Sie ihn aus. Wenn bereits eine Funktion aufgerufen wurde do_something, rufen Sie diese auf . Es ist im Allgemeinen wichtig, dass die Syntax eindeutig ist, aber es ist auch wichtig, dass es möglich ist, eine Funktion neu zu definieren, ohne sie versehentlich aufzurufen. Das Definieren und Aufrufen einer Funktion sollte nicht gleich aussehen.

Wie die Schale behandelt {und (.

Wie type {Sie sehen werden, {handelt es sich um ein Shell-Schlüsselwort. Das macht es so [[. Wenn es in einer Situation verwendet wird, in der es sich ansonsten um einen Befehl handeln würde, enthält es eine {spezielle Semantik. Insbesondere führt es eine Befehlsgruppierung durch. In anderen Situationen kann es jedoch ohne Fluchtzeichen verwendet werden, um ein wörtliches {Zeichen zu kennzeichnen. Dies schließt die Situation ein, dass es als zweites oder nachfolgendes Wort eines Befehls übergeben wird.

Natürlich Bash könnte entworfen wurden , zu behandeln , {anders als es gegenwärtig der Fall ist. Die Syntax wäre dann jedoch nicht mehr mit der POSIX-Shell kompatibel gewesen, und Bash wäre nicht wirklich eine Shell im Bourne-Stil und wäre nicht in der Lage, viele Shellskripten auszuführen.

Im Gegensatz dazu (handelt es sich um ein Shell-Metazeichen. Es wird immer speziell behandelt , wenn es in einem Befehl erscheint und nicht in Anführungszeichen gesetzt (mit ' ', " "oder \). Es gibt also keine Mehrdeutigkeit in der Syntax:

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

Das konnte nichts anderes bedeuten. Wenn Bash keine Funktionen hätte, wäre es ein Syntaxfehler, aus dem gleichen Grund echo foo(bar)ein Syntaxfehler.

Wenn Ihnen die ()Notation wirklich nicht gefällt, können Sie das Schlüsselwort verwenden functionund es weglassen, wenn Sudodus dies erwähnt . Beachten Sie, dass dies nicht Teil der Syntax zum Definieren von Funktionen in den meisten anderen Bourne-Shells ist. In einigen Fällen wird dies unterstützt, aber auf diese Weise definierte Funktionen haben eine andere Semantik. Daher ist ein Skript, das es verwendet, nicht portierbar. (Der Grund, warum diese Syntax eindeutig sein kann, ist, dass functiones sich bei dieser Syntax um ein Schlüsselwort in Bash handelt, das anzeigt, dass das, was darauf folgt, der Beginn einer Funktionsdefinition ist.)

Beachten Sie schließlich, dass die meisten Funktionsdefinitionen zwar {in der Praxis verwendet werden, jedoch alle zusammengesetzten Befehle zulässig sind. Wenn Sie eine Funktion hatten, deren Körper Sie immer in einer Unterschale ausführen wollten, könnten Sie ( )stattdessen verwenden { }.

Eliah Kagan
quelle
1
Auf einer höheren Ebene geht es um menschliche Erwartungen und Lesbarkeit. In den meisten Sprachen, insbesondere in C, fortran und anderen Sprachen, die zur Zeit der Entwicklung der Bash-Skriptsprachen üblich waren, enthält eine Funktion / Subroutine immer eine Argumentliste, möglicherweise leer, die in Klammern angegeben ist. Ändern Sie dieses Paradigma und Sie erschweren das Verstehen der Sprache.
Jamesqf
15

Das ()Token teilt dem Shell-Interpreter mit, dass Sie eine Funktion deklarieren.

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

Eine Alternative in bashistfunction

function do_something {
 echo 'do it'
}

oder als einzeiler kannst du testen

$ bash -c "function do_something { echo 'do it'; } ; do_something"
do it
Sudodus
quelle
2
Das functionSchlüsselwort ist ein Pre-POSIX-ksh-ism, das Bash aus Gründen der Abwärtskompatibilität unterstützt. Bash unterstützt dies jedoch nur schlecht (es werden keine funktionslokalen Standardeinstellungen für Variablen vorgenommen, wie dies bei alten ksh-Funktionen der Fall war, die in dieser Form deklariert wurden). Folglich ist es nicht ideal für neuen Code. Siehe wiki.bash-hackers.org/scripting/obsolete
Charles Duffy
@CharlesDuffy, Danke für diese Erklärung :-)
sudodus
1
@CharlesDuffy Sagen Sie, dass ksh und bash eine Syntax akzeptieren, die eine Variable in ksh lokal aber in bash global macht? Das klingt nicht richtig. Mein Verständnis, das teilweise auf diesem Beitrag und dieser Überprüfung (in ksh93) basiert, ist, dass ksh Variablen in weniger Situationen lokal macht als bash, niemals mehr. In bash deklariert typesetwithout -gin einer Funktion immer eine lokale Variable. Mit den functionScope-Variablen "keyword", "bash" und "ksh" verhalten Sie sich genauso , nicht wahr?
Eliah Kagan
@EliahKagan Ich denke, was Charles angesprochen hat, wurde in Gilles Antwort hier demonstriert
Sergiy Kolodyazhnyy
9

Wie sehr ein wahrer Zahnspangen- Stilist von dir!

Betrachten Sie andere, perfekt feine Zahnspangenstile:

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

Wie kann die Shell erkennen, dass es sich um Funktionsdefinitionen handelt und nicht nur um einen Befehl, fooauf den Befehlslisten folgen? In all diesen Fällen ist ein zusätzlicher eindeutiger Faktor wie ()oder das functionSchlüsselwort erforderlich.

muru
quelle
Zweitens ist das nicht OTBS, da die einzige Stelle, an der OTBS geschweifte Klammern in einer neuen Zeile zulässt, für Funktionsdefinitionen vorgesehen ist.
muru
3
Ich denke, es muss argumentiert werden, dass Sie es zu Recht als OTBS charakterisiert haben, da eine Funktionsdefinition in einem Bourne-Shell-Skript im Gegensatz zu Funktionen in C und C ++ direkt im Hauptteil einer anderen Funktionsdefinition verschachtelt werden kann verdient es nicht, ausgezeichnet zu werden. (Oder vielleicht ist dies der in der Java-Programmierung beliebte Stil, bei dem Methoden ihre öffnende Klammer in derselben Zeile und nicht in OTBS setzen.) In beiden Fällen können Sie hervorragend beurteilen, wie die Syntax die Platzierung der Klammern einschränken müsste, um zu funktionieren ganz ohne ()oder so ähnlich.
Eliah Kagan