Rückgabe eines Booleschen Werts von einer Bash-Funktion

211

Ich möchte eine Bash-Funktion schreiben, die prüft, ob eine Datei bestimmte Eigenschaften hat und true oder false zurückgibt. Dann kann ich es in meinen Skripten im "if" verwenden. Aber was soll ich zurückgeben?

function myfun(){ ... return 0; else return 1; fi;}

dann benutze ich es so:

if myfun filename.txt; then ...

Das funktioniert natürlich nicht. Wie kann dies erreicht werden?

luca
quelle
3
functionmyfun() {...}
Lassen Sie
2
Was zählt, ifist der Null-Exit-Status von myfun: Wenn myfunExits mit 0, then ...wird ausgeführt; wenn es etwas anderes else ... ist, wird ausgeführt.
Eelvex
7
@nhed: Das functionSchlüsselwort ist ein Bashismus und verursacht in einigen anderen Shells Syntaxfehler. Grundsätzlich ist es entweder unnötig oder verboten. Warum also? Es ist nicht einmal als Grep-Ziel nützlich, da es möglicherweise nicht vorhanden ist ( ()stattdessen Grep für ).
Gordon Davisson
3
@ GordonDavisson: Was? gibt es noch andere muscheln? ;-)
nhed
Bitte verwenden Sie nicht 0 und 1. Siehe stackoverflow.com/a/43840545/117471
Bruno Bronosky

Antworten:

333

Verwenden Sie 0 für wahr und 1 für falsch.

Stichprobe:

#!/bin/bash

isdirectory() {
  if [ -d "$1" ]
  then
    # 0 = true
    return 0 
  else
    # 1 = false
    return 1
  fi
}


if isdirectory $1; then echo "is directory"; else echo "nopes"; fi

Bearbeiten

Nach dem Kommentar von @ amichair sind diese ebenfalls möglich

isdirectory() {
  if [ -d "$1" ]
  then
    true
  else
    false
  fi
}


isdirectory() {
  [ -d "$1" ]
}
Erik
quelle
4
Nein, das müssen Sie nicht tun - sehen Sie sich das Beispiel an.
Erik
46
Zur besseren Lesbarkeit können Sie den Befehl 'true' (der nichts tut und erfolgreich abgeschlossen wird, dh 0 zurückgibt) und den Befehl 'false' (der nichts tut und erfolglos abschließt, dh einen Wert ungleich Null zurückgibt) verwenden. Außerdem gibt eine Funktion, die ohne explizite return-Anweisung endet, den Exit-Code des zuletzt ausgeführten Befehls zurück, sodass im obigen Beispiel der Funktionskörper nur auf reduziert werden kann [ -d "$1" ].
Amichair
24
Bengt: Es macht Sinn, wenn Sie es als "Fehlercode" betrachten: Fehlercode 0 = alles ging in Ordnung = 0 Fehler; Fehlercode 1 = Die Hauptsache, die dieser Aufruf ausführen sollte, ist fehlgeschlagen. sonst: scheitern! Schlagen Sie es in der Manpage nach.
fliegende Schafe
7
Es ist sinnvoll, wenn man bedenkt, dass Dinge beim Programmieren normalerweise nur auf eine Weise erfolgreich sein können, aber auf unendliche Weise scheitern können. Nun, vielleicht nicht unendlich, aber viele, die Chancen stehen gegen uns. Erfolg / Fehler sind nicht boolesch. Ich denke dies "Verwenden Sie 0 für wahr und 1 für falsch." sollte lauten "Verwenden Sie 0 für Erfolg und ungleich Null für Fehler".
Davos
6
Bitte verwenden Sie nicht 0 und 1. Siehe stackoverflow.com/a/43840545/117471
Bruno Bronosky
167

Warum sollte es dich interessieren, was ich sage, obwohl es eine Antwort von mehr als 250 Stimmen gibt?

Es ist nicht das 0 = trueund 1 = false. Es ist: Null bedeutet kein Fehler (Erfolg) und Nicht-Null bedeutet Fehler (vom Typ N) .

Während die ausgewählte Antwort technisch "wahr" ist, geben Sie bitte nicht return 1** in Ihren Code für falsch ein . Es wird mehrere unglückliche Nebenwirkungen haben.

  1. Erfahrene Entwickler werden Sie als Amateur erkennen (aus dem folgenden Grund).
  2. Erfahrene Entwickler tun dies nicht (aus den folgenden Gründen).
  3. Es ist fehleranfällig.
    • Selbst erfahrene Entwickler können 0 und 1 (aus dem oben genannten Grund) als falsch bzw. wahr verwechseln.
  4. Es erfordert (oder wird ermutigen) irrelevante und lächerliche Kommentare.
  5. Es ist eigentlich weniger hilfreich als implizite Rückgabestatus.

Lerne etwas Bash

Das Bash-Handbuch sagt (Hervorhebung von mir)

return [n]

Bewirkt, dass eine Shell-Funktion die Ausführung beendet und den Wert n an ihren Aufrufer zurückgibt. Wenn n nicht angegeben wird , ist der Rückgabewert der Exit-Status des zuletzt in der Funktion ausgeführten Befehls .

Daher müssen wir NIEMALS 0 und 1 verwenden, um Wahr und Falsch anzuzeigen. Die Tatsache, dass sie dies tun, ist im Wesentlichen triviales Wissen, das nur zum Debuggen von Code, zum Befragen von Fragen und zum Umblasen von Neulingen nützlich ist.

Das Bash-Handbuch sagt auch

Andernfalls ist der Rückgabestatus der Funktion der Beendigungsstatus des zuletzt ausgeführten Befehls

Das Bash-Handbuch sagt auch

( $? ) Wird auf den Exit-Status der zuletzt ausgeführten Vordergrund-Pipeline erweitert .

Whoa, warte. Pipeline? Wenden wir uns noch einmal dem Bash-Handbuch zu.

Eine Pipeline ist eine Folge von einem oder mehreren Befehlen, die durch einen der Steueroperatoren '|' getrennt sind. oder '| &'.

Ja. Sie sagten, 1 Befehl sei eine Pipeline. Daher sagen alle drei Zitate dasselbe.

  • $? sagt dir, was zuletzt passiert ist.
  • Es sprudelt.

Meine Antwort

Während @Kambus demonstrierte, dass mit einer so einfachen Funktion überhaupt keine returnbenötigt wird. Ich denke, es war unrealistisch einfach im Vergleich zu den Bedürfnissen der meisten Leute, die dies lesen werden.

Warum return?

Wenn eine Funktion den Exit-Status ihres letzten Befehls zurückgibt, warum returnüberhaupt verwenden? Weil dadurch eine Funktion nicht mehr ausgeführt wird.

Stoppen Sie die Ausführung unter mehreren Bedingungen

01  function i_should(){
02      uname="$(uname -a)"
03
04      [[ "$uname" =~ Darwin ]] && return
05
06      if [[ "$uname" =~ Ubuntu ]]; then
07          release="$(lsb_release -a)"
08          [[ "$release" =~ LTS ]]
09          return
10      fi
11
12      false
13  }
14
15  function do_it(){
16      echo "Hello, old friend."
17  }
18
19  if i_should; then
20    do_it
21  fi

Was wir hier haben ist ...

Zeile 04ist eine explizite [-ish] Rückgabe true, da die RHS von &&nur ausgeführt wird, wenn die LHS true war

Zeile 09gibt entweder wahr oder falsch zurück, was dem Status der Zeile entspricht08

Zeile 13gibt wegen Zeile falsch zurück12

(Ja, dies kann abgespielt werden, aber das gesamte Beispiel ist erfunden.)

Ein weiteres häufiges Muster

# Instead of doing this...
some_command
if [[ $? -eq 1 ]]; then
    echo "some_command failed"
fi

# Do this...
some_command
status=$?
if ! $(exit $status); then
    echo "some_command failed"
fi

Beachten Sie, wie das Setzen einer statusVariablen die Bedeutung von entmystifiziert $?. (Natürlich wissen Sie, was $?bedeutet, aber jemand, der weniger kenntnisreich ist als Sie, wird es eines Tages googeln müssen. Wenn Ihr Code keinen Hochfrequenzhandel betreibt, zeigen Sie etwas Liebe , setzen Sie die Variable.) Aber das eigentliche Mitnehmen ist das "wenn" Nicht vorhandener Status "oder umgekehrt" wenn Exit-Status "kann laut vorgelesen und deren Bedeutung erklärt werden. Allerdings kann das letzte ein wenig zu ehrgeizig sein , weil das Wort zu sehen , exitkönnten Sie es denken das Skript wird beendet, wenn es in Wirklichkeit die verlässt $(...)Subshell.


** Wenn Sie unbedingt darauf bestehen, return 1für false zu verwenden, empfehle ich Ihnen, return 255stattdessen mindestens zu verwenden . Dies führt dazu, dass Sie oder ein anderer Entwickler, der Ihren Code pflegen muss, die Frage stellen: "Warum ist das 255?" Dann werden sie zumindest aufpassen und eine bessere Chance haben, einen Fehler zu vermeiden.

Bruno Bronosky
quelle
1
@ZeroPhase 1 & 0 für false & true wäre lächerlich. Wenn Sie einen binären Datentyp haben, gibt es dafür keinen Grund. In Bash haben Sie es mit einem Statuscode zu tun , der Erfolg (Singular) und Misserfolg (Plural) widerspiegelt. Es ist " ifErfolg, mach das, elsemach das." Erfolg bei was? Könnte für wahr / falsch überprüft werden, könnte für eine Zeichenfolge, integer, Datei, Verzeichnis, Schreibrechte, glob, regex, grep, oder einen anderen Befehl wird überprüft , das ist vorbehaltlich Ausfälle .
Bruno Bronosky
3
Es ist dumm, die Standardverwendung von 0/1 als Rückgabewert zu vermeiden, nur weil sie stumpf und verwirrungsanfällig ist. Die gesamte Shell-Sprache ist stumpf und anfällig für Verwirrung. Bash selbst verwendet die Konvention 0/1 = true / false für sich trueund falseBefehle. Das heißt, das Schlüsselwort ergibt truebuchstäblich einen 0-Statuscode. Darüber hinaus arbeiten If-Then-Anweisungen von Natur aus mit Booleschen Werten und nicht mit Erfolgscodes. Wenn während einer booleschen Operation ein Fehler auftritt, sollte er nicht true oder false zurückgeben, sondern lediglich die Ausführung unterbrechen. Andernfalls erhalten Sie falsch positive Ergebnisse (Wortspiel).
Beejor
1
"if! $ (exit $ status); then" - Das verdient eine bessere Erklärung. Es ist nicht intuitiv. Ich muss überlegen, ob das Programm vor dem Drucken der Nachricht beendet wird oder nicht. Der Rest Ihrer Antwort war gut, aber das hat es verdorben.
Craig Hicks
1
@BrunoBronosky Ich habe Ihre Antwort gelesen und glaube, ich verstehe den Unterschied zwischen der Inhaltspipeline (stdin-> stdout) und den Fehlercodes in den Rückgabewerten. Ich verstehe jedoch nicht, warum die Verwendung return 1vermieden werden sollte. Angenommen, ich habe eine validateFunktion, die ich für sinnvoll halte, return 1wenn die Validierung fehlschlägt. Dies ist schließlich ein Grund, die Ausführung eines Skripts zu stoppen, wenn es nicht ordnungsgemäß behandelt wird (z. B. bei Verwendung set -e).
JepZ
1
@Jepz das ist eine tolle Frage. Sie haben Recht, das return 1ist gültig. Mein Anliegen sind alle Kommentare hier, die besagen: "0 = wahr 1 = falsch ist [negatives Wort einfügen]". Diese Leute werden wahrscheinlich eines Tages Ihren Code lesen. Für sie sollte das Sehen return 255(oder 42 oder sogar 2) ihnen helfen, mehr darüber nachzudenken und es nicht als „wahr“ zu verwechseln. set -ewerde es immer noch fangen.
Bruno Bronosky
31
myfun(){
    [ -d "$1" ]
}
if myfun "path"; then
    echo yes
fi
# or
myfun "path" && echo yes
Kambus
quelle
1
Was ist mit Negation?
Einpoklum
Was ist damit, @einpoklum?
Mark Reed
@ MarkReed: Ich meinte, fügen Sie Ihrem Beispiel den Fall "else" hinzu.
Einpoklum
myfun "Pfad" || Echo Nein
Hrobky
13

Seien Sie vorsichtig, wenn Sie das Verzeichnis nur mit der Option -d überprüfen!
Wenn die Variable $ 1 leer ist, ist die Prüfung weiterhin erfolgreich. Stellen Sie sicher, dass die Variable nicht leer ist.

#! /bin/bash

is_directory(){

    if [[ -d $1 ]] && [[ -n $1 ]] ; then
        return 0
    else
        return 1
    fi

}


#Test
if is_directory $1 ; then
    echo "Directory exist"
else
    echo "Directory does not exist!" 
fi
RenRen
quelle
1
Ich bin mir nicht sicher, wie dies die gestellte Frage beantwortet. Es ist zwar schön zu wissen, dass ein leeres $ 1 ein wahres zurückgeben kann, wenn es leer ist, aber es gibt keinen Einblick, wie wahr oder falsch von einer Bash-Funktion zurückgegeben werden kann. Ich würde vorschlagen, eine neue Frage zu erstellen: "Was passiert, wenn Sie einen Test für eine leere Shell-Variable durchführen?" Und dann poste dies als Antwort.
DRaehal
3
Beachten Sie, dass Sie nicht nach einer leeren Variablen suchen müssen, wenn Sie $1( "$1") ein korrektes Anführungszeichen hinzufügen . Das [[ -d "$1" ]]würde fehlschlagen, da dies ""kein Verzeichnis ist.
Morgents
3

Ich bin auf einen Punkt gestoßen (noch nicht explizit erwähnt?), Über den ich gestolpert bin. Das heißt, nicht , wie man das Rück die boolean, sondern wie man es richtig zu bewerten!

Ich habe versucht zu sagen if [ myfunc ]; then ..., aber das ist einfach falsch. Sie dürfen die Klammern nicht verwenden! if myfunc; then ...ist der Weg, es zu tun.

Wie bei @Bruno und anderen wiederholt, trueund falsesind Befehle , keine Werte! Dies ist sehr wichtig, um Boolesche Werte in Shell-Skripten zu verstehen.

In diesem Beitrag habe ich boolesche Variablen erklärt und demonstriert : https://stackoverflow.com/a/55174008/3220983 . Ich empfehle dringend, dies zu überprüfen, da es so eng miteinander verbunden ist.

Hier werde ich einige Beispiele für die Rückgabe und Auswertung von Booleschen Werten aus Funktionen geben:

Dies:

test(){ false; }                                               
if test; then echo "it is"; fi                                 

Erzeugt keine Echoausgabe. (dh false gibt false zurück)

test(){ true; }                                                
if test; then echo "it is"; fi                                 

Produziert:

it is                                                        

(dh true gibt true zurück)

Und

test(){ x=1; }                                                
if test; then echo "it is"; fi                                 

Produziert:

it is                                                                           

Weil 0 (dh wahr) implizit zurückgegeben wurde .

Das hat mich vermasselt ...

test(){ true; }                                                
if [ test ]; then echo "it is"; fi                             

Produziert:

it is                                                                           

UND

test(){ false; }                                                
if [ test ]; then echo "it is"; fi                             

AUCH produziert:

it is                                                                           

Die Verwendung der Klammern hier ergab ein falsches Positiv ! (Ich schließe daraus, dass das "äußere" Befehlsergebnis 0 ist.)

Die wichtigste Auswirkung meines Beitrags ist: Verwenden Sie keine Klammern, um eine boolesche Funktion (oder Variable) auszuwerten, wie Sie es für eine typische Gleichheitsprüfung tun würden, z if [ x -eq 1 ]; then....

BuvinJ
quelle
2

Verwenden Sie die Befehle trueoder falseunmittelbar vor Ihrem return, dann returnohne Parameter. Der verwendet returnautomatisch den Wert Ihres letzten Befehls.

Das Bereitstellen von Argumenten für returnist inkonsistent, typspezifisch und fehleranfällig, wenn Sie nicht 1 oder 0 verwenden. Wie bereits in früheren Kommentaren erwähnt, ist die Verwendung von 1 oder 0 hier nicht der richtige Weg, um sich dieser Funktion zu nähern.

#!/bin/bash

function test_for_cat {
    if [ $1 = "cat" ];
    then
        true
        return
    else
        false
        return
    fi
}

for i in cat hat;
do
    echo "${i}:"
    if test_for_cat "${i}";
    then
        echo "- True"
    else
        echo "- False"
    fi
done

Ausgabe:

$ bash bash_return.sh

cat:
- True
hat:
- False
mrteatime
quelle
1

Es könnte funktionieren, wenn Sie dies function myfun(){ ... return 0; else return 1; fi;}wie folgt umschreiben function myfun(){ ... return; else false; fi;}. Das heißt, wenn dies falsedie letzte Anweisung in der Funktion ist, erhalten Sie ein falsches Ergebnis für die gesamte Funktion, returnunterbrechen jedoch die Funktion trotzdem mit dem wahren Ergebnis. Ich glaube, das gilt zumindest für meinen Bash-Dolmetscher.

Neffe
quelle
1

Ich fand die kürzeste Form zum Testen der Funktionsausgabe einfach

do_something() {
    [[ -e $1 ]] # e.g. test file exists
}

do_something "myfile.txt" || { echo "File doesn't exist!"; exit 1; }
Roy Shilkrot
quelle
1

Aus Gründen der Lesbarkeit des Codes sollte meines Erachtens die Rückgabe von true / false:

  • in einer Zeile sein
  • sei ein Befehl
  • sei leicht zu merken
  • Erwähnen Sie das Schlüsselwort, returngefolgt von einem anderen Schlüsselwort ( trueoder false)

Meine Lösung ist return $(true)oder return $(false)wie gezeigt:

is_directory()
{
    if [ -d "${1}" ]; then
        return $(true)
    else
        return $(false)
    fi
}
Charlie
quelle
0

Nach @Bruno Bronosky und @mrteatime schlage ich vor, dass Sie Ihre boolesche Rückgabe einfach "rückwärts" schreiben. Das ist was ich meine:

foo()
{
    if [ "$1" == "bar" ]; then
        true; return
    else
        false; return
    fi;
}

Dadurch entfällt die hässliche zweizeilige Anforderung für jede return-Anweisung.

BuvinJ
quelle