Rückgabe des Werts der aufgerufenen Funktion in einem Shell-Skript

126

Ich möchte den Wert von einer Funktion zurückgeben, die in einem Shell-Skript aufgerufen wird. Vielleicht fehlt mir die Syntax. Ich habe versucht, die globalen Variablen zu verwenden. Das funktioniert aber auch nicht. Der Code lautet:

lockdir="somedir"
test() {
    retval=""

    if mkdir "$lockdir"
        then    # Directory did not exist, but it was created successfully
            echo >&2 "successfully acquired lock: $lockdir"
            retval="true"
        else
            echo >&2 "cannot acquire lock, giving up on $lockdir"
            retval="false"
    fi
    return retval
}


retval=test()
if [ "$retval" == "true" ]
    then
        echo "directory not created"
    else
        echo "directory already created"
fi
Mridul Vishal
quelle
Nicht im Zusammenhang mit Ihrer Frage, aber trotzdem ... Wenn Sie versuchen, eine Sperre zu erhalten, können Sie den Befehl "lockfile" verwenden.
Víctor Herraiz

Antworten:

277

Eine Bash-Funktion kann einen String nicht direkt so zurückgeben, wie Sie es möchten. Sie können drei Dinge tun:

  1. Echo eine Zeichenfolge
  2. Gibt einen Exit-Status zurück, bei dem es sich um eine Zahl und nicht um eine Zeichenfolge handelt
  3. Teilen Sie eine Variable

Dies gilt auch für einige andere Muscheln.

So führen Sie jede dieser Optionen aus:

1. Echo Saiten

lockdir="somedir"
testlock(){
    retval=""
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval="true"
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval="false"
    fi
    echo "$retval"
}

retval=$( testlock )
if [ "$retval" == "true" ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2. Geben Sie den Exit-Status zurück

lockdir="somedir"
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
    return "$retval"
}

testlock
retval=$?
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

3. Variable teilen

lockdir="somedir"
retval=-1
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
}

testlock
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi
olibre
quelle
2
Verwenden Sie kein functionSchlüsselwort, um eine Bash-Funktion zu definieren. Das würde es weniger tragbar machen. Entfernen Sie es.
Dimir
2
In Ihrem dritten Beispiel ist retval keine Umgebungsvariable. Es ist lediglich eine Shell-Variable. Es wird nur dann zu einer Umgebungsvariablen, wenn Sie es exportieren. Vielleicht sollte der Titel des dritten Beispiels "globale Variable" anstelle von "Umgebungsvariable" sein.
William Pursell
4
Im zweiten Beispiel ist es idiomatischer, "wenn testlock; dann ..." zu schreiben, anstatt von $? Zuzuweisen
William Pursell
@WilliamPursell Ich habe das falsche Wort "Umgebung" entfernt. Lassen Sie uns "$?" für pädagogische Zwecke. Ich habe die Wiki-Community aktiviert, daher steht es euch allen frei, die Antwort zu verbessern
;-)
1
@ManuelJordan, Funktionen können nur Exit-Codes und> & 2-Protokolle an stderror zurückgeben. Das letzte Echo wird also in stdout geschrieben. Die aufrufende Funktion erfasst also NUR stdout und nicht stderr. Angenommen, die Ausführung ist Single-Threaded, ist es eine bessere Option, eine benutzerdefinierte Variable wie TEST_LOCK_STATUS = "" außerhalb der Methode beizubehalten, die jeder nach dem Aufruf von testlock verwenden kann, und sie jedes Mal zu Beginn der Methode zurückzusetzen
kisna
16

Sie arbeiten viel zu hart. Ihr gesamtes Skript sollte sein:

if mkdir "$lockdir" 2> /dev/null; then 
  echo lock acquired
else
  echo could not acquire lock >&2
fi

aber auch das ist wohl zu ausführlich. Ich würde es codieren:

mkdir "$lockdir" || exit 1

Die resultierende Fehlermeldung ist jedoch etwas unklar.

William Pursell
quelle
1
Die fehlende Fehlermeldung lässt sich leicht beheben, obwohl sie etwas ausführlicher ist: mkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; }(Beachten Sie die ;vor der schließenden geschweiften Klammer). Außerdem definiere ich häufig eine Fehlerfunktion, die einen optionalen Nachrichtenparameter verwendet, der an stderr gedruckt und dann mit dem Rückkehrcode 1 beendet wird, sodass ich den besser lesbaren verwenden kann mkdir "$lockdir" || fail "could not create lock dir".
Blubberdiblub
@blubberdiblub: aber die Fail-Funktion kann die "aktuelle" Funktion oder das "aktuelle" Skript nicht beenden, oder? Sie müssten also verwenden, cmd || fail "error msg" || return 1wenn Sie das möchten, oder?
Max
@Max nicht die aktuelle Funktion, das ist richtig. Das aktuelle Skript wird jedoch beendet, solange Sie es als Befehl aufgerufen und nicht als Quelle verwendet haben. Ich denke normalerweise an eine solche failFunktion, die nur für tödliche Situationen verwendet wird.
blubberdiblub
12

Wenn es sich nur um einen Richtig / Falsch-Test handelt, haben Sie Ihre Funktion return 0für Erfolg und return 1Misserfolg. Der Test wäre dann:

if function_name; then
  do something
else
  error condition
fi
Glenn Jackman
quelle
Genau das, wonach ich gesucht habe.
Samuel
Gibt es eine Möglichkeit, diese Notation auch für parametrisierte Funktionen zu verwenden?
Alex
@alex können Sie ein Beispiel geben, was Sie unter "parametrisierter Funktion" verstehen?
Glenn Jackman
'myCopyFunc $ {SOURCE} $ {DEST}', gibt bei Erfolg 0 zurück. ZB wie in dieser Ausgabe: stackoverflow.com/questions/6212219/…
Alex
Ja, das ist vollkommen in Ordnung
Glenn Jackman
2

Ich denke, 0 für Succ / 1 für Fail (Glenn Jackman) zurückzugeben und Olibres klare und erklärende Antwort sagt alles; Nur um eine Art "Combo" -Ansatz für Fälle zu erwähnen, in denen die Ergebnisse nicht binär sind und Sie lieber eine Variable festlegen möchten, als ein Ergebnis "wiederzugeben" (wenn Ihre Funktion beispielsweise AUCH etwas wiedergibt, wird dieser Ansatz dies tun nicht arbeiten). Was dann? (unten ist Bourne Shell)

# Syntax _w (wrapReturn)
# arg1 : method to wrap
# arg2 : variable to set
_w(){
eval $1
read $2 <<EOF
$?
EOF
eval $2=\$$2
}

wie in (yep, das Beispiel ist etwas albern, es ist nur ein .. Beispiel)

getDay(){
  d=`date '+%d'`
  [ $d -gt 255 ] && echo "Oh no a return value is 0-255!" && BAIL=0 # this will of course never happen, it's just to clarify the nature of returns
  return $d
}

dayzToSalary(){
  daysLeft=0
  if [ $1 -lt 26 ]; then 
      daysLeft=`expr 25 - $1`
  else
     lastDayInMonth=`date -d "`date +%Y%m01` +1 month -1 day" +%d`
     rest=`expr $lastDayInMonth - 25`
     daysLeft=`expr 25 + $rest`
  fi
  echo "Mate, it's another $daysLeft days.."
}

# main
_w getDay DAY # call getDay, save the result in the DAY variable
dayzToSalary $DAY
Ola Aronsson
quelle
1

Falls Sie einige Parameter an eine Funktion übergeben müssen und einen Wert zurückgeben möchten. Hier übergebe ich "12345" als Argument an eine Funktion und nach Verarbeitung der zurückgegebenen Variablen XYZ, die VALUE zugewiesen wird

#!/bin/bash
getValue()
{
    ABC=$1
    XYZ="something"$ABC
    echo $XYZ
}


VALUE=$( getValue "12345" )
echo $VALUE

Ausgabe:

something12345
Rishi Bansal
quelle