Ich möchte einen String von einer Bash-Funktion zurückgeben.
Ich werde das Beispiel in Java schreiben, um zu zeigen, was ich tun möchte:
public String getSomeString() {
return "tadaa";
}
String variable = getSomeString();
Das folgende Beispiel funktioniert in Bash, aber gibt es einen besseren Weg, dies zu tun?
function getSomeString {
echo "tadaa"
}
VARIABLE=$(getSomeString)
string
bash
function
return-value
Tomas F.
quelle
quelle
function funcName {
ist die Legacy-Syntax vor POSIX vom frühen ksh geerbt (wo es semantische Unterschiede gab, die bash nicht berücksichtigt).funcName() {
, mit nofunction
, sollte stattdessen verwendet werden; siehe wiki.bash-hackers.org/scripting/obsoletefunction myFunction { blah; }
ist gut; Esfunction myFunction() { blah }
ist nicht in Ordnung, dh die Verwendung von Klammern mit der Schlüsselwortfunktion.Antworten:
Es gibt keinen besseren Weg, den ich kenne. Bash kennt nur Statuscodes (Ganzzahlen) und Zeichenfolgen, die in die Standardausgabe geschrieben wurden.
quelle
somefunction && echo 'success'
). Wenn Sie sich eine Funktion wie einen anderen Befehl vorstellen, ist dies sinnvoll. Befehle "geben" beim Beenden nichts anderes als einen Statuscode zurück, können jedoch in der Zwischenzeit Dinge ausgeben, die Sie erfassen können.Sie können die Funktion eine Variable als erstes Argument verwenden lassen und die Variable mit der Zeichenfolge ändern, die Sie zurückgeben möchten.
Druckt "foo bar rab oof".
Bearbeiten : An der entsprechenden Stelle wird ein Anführungszeichen hinzugefügt, damit Leerzeichen in der Zeichenfolge den Kommentar von @Luca Borrione adressieren können.
Bearbeiten : Zur Demonstration sehen Sie das folgende Programm. Dies ist eine Allzwecklösung: Sie können sogar eine Zeichenfolge in eine lokale Variable empfangen.
Dies druckt:
Bearbeiten : was zeigt , dass der ursprüngliche Variablenwert ist in der Funktion zur Verfügung, wie falsch von @Xichen Li in einem Kommentar kritisiert wurde.
Dies gibt Ausgabe:
quelle
fgm
Antwort, die auf vereinfachte Weise geschrieben wurde? Dies funktioniert nicht, wenn die Zeichenfolgefoo
Leerzeichen enthält, während die Zeichenfolgefgm
.. wie er zeigt.\$$1
. Wenn Sie etwas anderes suchen, lassen Sie es mich bitte wissen.printf '%q' "$var"
. % q ist eine Formatzeichenfolge für das Shell-Escape. Dann gib es einfach roh weiter.Alle obigen Antworten ignorieren, was in der Manpage von bash angegeben wurde.
Beispielcode
Und Ausgabe
Auch unter pdksh und ksh macht dieses Skript dasselbe!
quelle
Bash bietet seit Version 4.3, Februar 2014 (?), Eine explizite Unterstützung für Referenzvariablen oder Namensreferenzen (namerefs), die über "eval" hinausgehen, mit derselben vorteilhaften Leistung und demselben Indirektionseffekt, die in Ihren Skripten möglicherweise klarer und auch schwieriger sind um "zu vergessen, 'auszuwerten' und diesen Fehler zu beheben":
und auch:
Zum Beispiel ( EDIT 2 : (danke Ron) Namespace (vorangestellt) des funktionsinternen Variablennamens, um externe Variablenkonflikte zu minimieren, die das in den Kommentaren von Karsten aufgeworfene Problem endgültig beantworten sollten):
und Testen dieses Beispiels:
Beachten Sie, dass die eingebaute Bash "deklarieren" bei Verwendung in einer Funktion die deklarierte Variable standardmäßig "lokal" macht und "-n" auch mit "lokal" verwendet werden kann.
Ich bevorzuge es, "wichtige Deklarations" -Variablen von "langweiligen lokalen" Variablen zu unterscheiden, daher dient die Verwendung von "Deklaration" und "lokal" auf diese Weise als Dokumentation.
EDIT 1 - (Antwort auf Kommentar unten von Karsten) - Ich kann unten keine Kommentare mehr hinzufügen, aber Karstens Kommentar hat mich zum Nachdenken gebracht, also habe ich den folgenden Test durchgeführt, der FUNKTIONIERT, AFAICT - Karsten, wenn Sie dies lesen, geben Sie bitte einen genauen Satz an von Testschritten über die Befehlszeile, die das von Ihnen angenommene Problem anzeigen, da diese folgenden Schritte einwandfrei funktionieren:
(Ich habe dies gerade ausgeführt, nachdem ich die obige Funktion in einen Bash-Begriff eingefügt habe - wie Sie sehen können, funktioniert das Ergebnis einwandfrei.)
quelle
declare
lokale Variablen innerhalb von Funktionen erstellt werden (diese Informationen werden nicht von angegebenhelp declare
): "... Wenn Sie in einer Funktion verwendet werden, deklarieren und setzen Sie jeden Namen lokal, wie beim lokalen Befehl, es sei denn, - g Option wird mitgeliefert ... "K=$1; V=$2; eval "$A='$V'";
, aber einen Fehler (z. B. einen leeren oder ausgelassenen Parameter), und es wäre gefährlicher. @zenaan Das von @Karsten aufgeworfene Problem tritt auf, wenn Sie "Nachricht" als Namen der Rückgabevariable anstelle von "Ret" auswählen.Wie oben bei bstpierre verwende und empfehle ich die explizite Benennung von Ausgabevariablen:
Beachten Sie die Verwendung der Angabe des $. Dadurch wird vermieden, dass Inhalte
$result
als Shell-Sonderzeichen interpretiert werden . Ich habe festgestellt, dass dies eine Größenordnung schneller ist als dieresult=$(some_func "arg1")
Redewendung, ein Echo zu erfassen. Der Geschwindigkeitsunterschied scheint bei Verwendung von Bash auf MSYS noch bemerkenswerter zu sein, wo die Standarderfassung von Funktionsaufrufen fast katastrophal ist.Es ist in Ordnung, lokale Variablen einzusenden, da die Einheimischen dynamisch in Bash festgelegt sind:
quelle
echo
Inneres einer Funktion mit einer Befehlssubstitution zu kombinieren?Sie können auch die Funktionsausgabe erfassen:
Sieht komisch aus, ist aber meiner Meinung nach besser als die Verwendung globaler Variablen. Das Übergeben von Parametern funktioniert wie gewohnt. Setzen Sie sie einfach in die geschweiften Klammern oder Backticks.
quelle
Die einfachste und robusteste Lösung ist die Verwendung der Befehlssubstitution, wie andere geschrieben haben:
Der Nachteil ist die Leistung, da dies einen separaten Prozess erfordert.
Die andere in diesem Thema vorgeschlagene Technik, nämlich die Übergabe des Namens einer Variablen, der als Argument zugewiesen werden soll, hat Nebenwirkungen, und ich würde sie in ihrer Grundform nicht empfehlen. Das Problem ist, dass Sie wahrscheinlich einige Variablen in der Funktion benötigen, um den Rückgabewert zu berechnen, und es kann vorkommen, dass der Name der Variablen, die den Rückgabewert speichern soll, eine davon stört:
Sie können interne Variablen der Funktion natürlich nicht als lokal deklarieren, aber Sie sollten dies wirklich immer tun, da Sie andernfalls versehentlich eine nicht verwandte Variable aus dem übergeordneten Bereich überschreiben können, wenn es eine mit demselben Namen gibt .
Eine mögliche Problemumgehung ist eine explizite Deklaration der übergebenen Variablen als global:
Wenn der Name "x" als Argument übergeben wird, überschreibt die zweite Zeile des Funktionskörpers die vorherige lokale Deklaration. Die Namen selbst können jedoch weiterhin stören. Wenn Sie also den zuvor in der übergebenen Variablen gespeicherten Wert verwenden möchten, bevor Sie den Rückgabewert dort schreiben, müssen Sie ihn ganz am Anfang in eine andere lokale Variable kopieren. Andernfalls ist das Ergebnis unvorhersehbar! Außerdem funktioniert dies nur in der neuesten Version von BASH, nämlich 4.2. Portablerer Code verwendet möglicherweise explizite bedingte Konstrukte mit demselben Effekt:
Die vielleicht eleganteste Lösung besteht darin, nur einen globalen Namen für Funktionsrückgabewerte zu reservieren und ihn in jeder von Ihnen geschriebenen Funktion konsistent zu verwenden.
quelle
eval
als auch bei derdeclare -n
Lösung. Die Problemumgehung, einen einzigen dedizierten Variablennamen wieresult
für alle Ausgabeparameter zu haben, scheint die einzige Lösung zu sein, für die keine Funktionen erforderlich sind, um alle Aufrufer zu kennen, um Konflikte zu vermeiden.Wie bereits erwähnt, ist die "richtige" Möglichkeit, eine Zeichenfolge von einer Funktion zurückzugeben, die Befehlssubstitution. Für den Fall, dass die Funktion auch an die Konsole ausgegeben werden muss (wie oben von @Mani erwähnt), erstellen Sie am Anfang der Funktion ein temporäres fd und leiten Sie zur Konsole um. Schließen Sie das temporäre fd, bevor Sie Ihren String zurückgeben.
Das Ausführen eines Skripts ohne Parameter erzeugt ...
hoffe, das hilft den Menschen
-Andy
quelle
3>&1
am Kopf des Skripts duplizieren&1
&3
und dann einen weiteren Platzhalter&4
innerhalb der Funktion bearbeiten . Überall hässlich.Sie können eine globale Variable verwenden:
Das gibt
quelle
Um meinen Kommentar zu Andys Antwort zu veranschaulichen, mit zusätzlicher Manipulation des Dateideskriptors, um die Verwendung von
/dev/tty
: zu vermeiden.Trotzdem böse.
quelle
Die Art und Weise, wie Sie es haben, ist die einzige Möglichkeit, dies zu tun, ohne den Umfang zu beeinträchtigen. Bash hat kein Konzept für Rückgabetypen, sondern nur Exit-Codes und Dateideskriptoren (stdin / out / err usw.)
quelle
Adressierung von Vicky Ronnens Kopf hoch unter Berücksichtigung des folgenden Codes:
wird geben
Möglicherweise besteht das normale Szenario darin, die in der
test_inside_a_func
Funktion verwendete Syntax zu verwenden. In den meisten Fällen können Sie daher beide Methoden verwenden. Die Erfassung der Ausgabe ist jedoch die sicherere Methode, die in jeder Situation immer funktioniert und den Rückgabewert einer Funktion nachahmt, die Sie können in anderen Sprachen finden, wieVicky Ronnen
richtig hervorgehoben.quelle
Ich denke, die Optionen wurden alle aufgezählt. Die Auswahl eines Stils hängt möglicherweise vom besten Stil für Ihre spezielle Anwendung ab. In diesem Sinne möchte ich einen bestimmten Stil anbieten, den ich für nützlich befunden habe. In Bash befinden sich Variablen und Funktionen nicht im selben Namespace. Die Behandlung der gleichnamigen Variablen als Wert der Funktion ist eine Konvention, die meiner Meinung nach Namenskonflikte minimiert und die Lesbarkeit verbessert, wenn ich sie konsequent anwende. Ein Beispiel aus dem wirklichen Leben:
Ein Beispiel für die Verwendung solcher Funktionen:
Wie Sie sehen, können Sie den Rückgabestatus bei Bedarf verwenden oder ignorieren, wenn Sie dies nicht tun. Die "zurückgegebene" Variable kann ebenfalls verwendet oder ignoriert werden, aber natürlich erst, nachdem die Funktion aufgerufen wurde.
Dies ist natürlich nur eine Konvention. Es steht Ihnen frei, den zugehörigen Wert vor der Rückkehr nicht festzulegen (daher meine Konvention, ihn immer am Anfang der Funktion auf Null zu setzen) oder seinen Wert durch erneutes Aufrufen der Funktion (möglicherweise indirekt) mit Füßen zu treten. Trotzdem ist es eine Konvention, die ich sehr nützlich finde, wenn ich Bash-Funktionen stark nutze.
Im Gegensatz zu dem Gefühl, dass dies ein Zeichen ist, das man z. B. "Perl bewegen" sollte, ist meine Philosophie, dass Konventionen immer wichtig sind, um die Komplexität einer Sprache zu verwalten.
quelle
Sie können
echo
einen String, aber fangen Sie ihn durch Piping (|
die Funktion an etwas anderes ).Sie können dies tun
expr
, obwohl ShellCheck diese Verwendung als veraltet meldet.quelle
myfunc | read OUTPUT ; echo $OUTPUT
ergibt sich nichts.myfunc | ( read OUTPUT; echo $OUTPUT )
erhält den erwarteten Wert und verdeutlicht, was auf der rechten Seite passiert. Aber natürlich ist OUTPUT dann nicht dort verfügbar, wo Sie es brauchen ...Das Hauptproblem eines Schemas mit 'benannter Ausgabevariable', bei dem der Aufrufer den Variablennamen (unabhängig davon, ob er
eval
oder verwendetdeclare -n
) übergibt, ist versehentliches Aliasing, dh Namenskonflikte: Aus Sicht der Kapselung ist es schrecklich, nicht hinzufügen oder umbenennen zu können Eine lokale Variable in einer Funktion, ohne zuerst ALLE Aufrufer der Funktion zu überprüfen , um sicherzustellen, dass sie nicht denselben Namen wie der Ausgabeparameter übergeben möchten. (Oder in die andere Richtung möchte ich nicht die Quelle der aufgerufenen Funktion lesen müssen, nur um sicherzustellen, dass der Ausgabeparameter, den ich verwenden möchte, in dieser Funktion nicht lokal ist.)Die einzige Möglichkeit, dies zu umgehen , besteht darin, eine einzelne dedizierte Ausgabevariable wie
REPLY
(wie von Evi1M4chine vorgeschlagen ) oder eine Konvention wie die von Ron Burk vorgeschlagene zu verwenden .Es ist jedoch möglich, dass Funktionen intern eine feste Ausgabevariable verwenden und dann etwas Zucker darüber hinzufügen, um diese Tatsache vor dem Aufrufer zu verbergen , wie ich es mit der
call
Funktion im folgenden Beispiel getan habe . Betrachten Sie dies als Proof of Concept, aber die wichtigsten Punkte sindREPLY
und kann wie gewohnt auch einen Exit-Code zurückgebenREPLY
(siehewrapper
Beispiel). Der Exit-Code der Funktion wird durchlaufen, also verwenden Sie sie zB in einemif
oderwhile
oder ähnliche Konstrukte , wie erwartet funktioniert.Der Grund dafür ist, dass die
call
Funktion selbst keine Einheimischen hat und keine anderen Variablen verwendet alsREPLY
, um mögliche Namenskonflikte zu vermeiden. An dem Punkt, an dem der vom Aufrufer definierte Name der Ausgabevariable zugewiesen wird, befinden wir uns effektiv im Bereich des Aufrufers (technisch im identischen Bereich dercall
Funktion) und nicht im Bereich der aufgerufenen Funktion.Ausgabe:
quelle
Bash- Muster, um sowohl skalare als auch Array- Wert-Objekte zurückzugeben:
Definition
Aufruf
quelle
In meinen Programmen ist dies konventionell der Zweck der bereits vorhandenen
$REPLY
Variablen, dieread
genau für diesen Zweck verwendet wird.Das ist
echo
esUm Konflikte zu vermeiden, reicht jedoch jede andere globale Variable aus.
Wenn das nicht ausreicht, empfehle ich die Lösung von Markarian451 .
quelle
quelle