Restituzione del valore dalla funzione chiamata in uno script di shell


126

Voglio restituire il valore da una funzione chiamata in uno script di shell. Forse mi manca la sintassi. Ho provato a utilizzare le variabili globali. Ma anche questo non funziona. Il codice è:

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

Non correlato alla tua domanda, ma comunque ... se stai cercando di ottenere un blocco puoi usare il comando "lockfile".
Víctor Herraiz

Risposte:


277

Una funzione Bash non può restituire una stringa direttamente come desideri. Puoi fare tre cose:

  1. Eco una stringa
  2. Restituisce uno stato di uscita, che è un numero, non una stringa
  3. Condividi una variabile

Questo vale anche per alcune altre conchiglie.

Ecco come eseguire ciascuna di queste opzioni:

1. Corde di eco

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. Restituire lo stato di uscita

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. Condividi variabile

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

2
Non utilizzare una functionparola chiave per definire una funzione bash. Ciò lo renderebbe meno portatile. Rimuovendolo.
dimir

2
Nel terzo esempio, retval non è una variabile di ambiente. È semplicemente una variabile di shell. Diventerà una variabile d'ambiente solo se la esporti. Forse il titolo del terzo esempio dovrebbe essere "variabile globale" invece di "variabile d'ambiente".
William Pursell

4
Nel secondo esempio, invece di assegnare da $?, È più idiomatico scrivere "if testlock; then ..."
William Pursell

@ WilliamPursell Ho rimosso la parola "ambiente" sbagliata. Manteniamo "$?" per scopi pedagogici. Ho abilitato la comunità Wiki, quindi siete tutti liberi di migliorare la risposta ;-)
olibre

1
@ManuelJordan, Functions può restituire solo codici di uscita e> & 2 log a stderror, quindi l'ultimo echo viene scritto su stdout, quindi la funzione chiamante cattura SOLO stdout e non stderr. Supponendo che l'esecuzione sia a thread singolo, un'opzione migliore è mantenere una variabile personalizzata specifica come TEST_LOCK_STATUS = "" al di fuori del metodo che chiunque può utilizzare dopo aver chiamato testlock e reimpostarlo ogni volta all'inizio del metodo
kisna

16

Stai lavorando troppo duramente. L'intero script dovrebbe essere:

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

ma anche questo è probabilmente troppo prolisso. Lo codificherei:

mkdir "$lockdir" || exit 1

ma il messaggio di errore risultante è un po 'oscuro.


1
Il messaggio di errore mancante è abbastanza facile da correggere, anche se è leggermente più dettagliato: mkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; }(nota ;prima della parentesi graffa di chiusura). Inoltre, spesso definisco una funzione di errore che accetta un parametro di messaggio opzionale che stampa su stderr e quindi esce con il codice di ritorno 1, permettendomi di utilizzare il più leggibile mkdir "$lockdir" || fail "could not create lock dir".
blubberdiblub

@blubberdiblub: ma la funzione fail non può uscire dalla funzione o dallo script "corrente", vero? quindi dovresti usarlo cmd || fail "error msg" || return 1se lo desideri, vero?
Max

@ Max non è la funzione corrente, è corretto. Ma uscirà dallo script corrente, purché tu lo chiami come comando e non lo abbia originato . Di solito penso a una tale failfunzione usata solo per situazioni fatali.
blubberdiblub

12

Se è solo un test vero / falso, scegli la tua funzione return 0per il successo e return 1per il fallimento. Il test sarebbe quindi:

if function_name; then
  do something
else
  error condition
fi

Esattamente quello che stavo cercando.
Samuel

C'è un modo per utilizzare questa notazione anche per le funzioni parametrizzate?
Alex

@alex puoi fare un esempio di cosa intendi per "funzione parametrizzata"?
glenn jackman

"myCopyFunc $ {SOURCE} $ {DEST}", restituisce 0 in caso di successo. Ad esempio, come in questo numero: stackoverflow.com/questions/6212219/…
Alex

Sì, va benissimo
glenn jackman il

2

Penso che restituire 0 per succ / 1 per fallire (glenn jackman) e la risposta chiara ed esplicativa di olibre dica tutto; solo per citare una sorta di approccio "combinato" per i casi in cui i risultati non sono binari e preferiresti impostare una variabile piuttosto che "echeggiare" un risultato (ad esempio se la tua funzione suppone ANCHE di echeggiare qualcosa, questo approccio lo farà non funziona). Cosa poi? (sotto è Bourne Shell)

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

come in (sì, l'esempio è un po 'sciocco, è solo un .. esempio)

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

1

Nel caso tu abbia alcuni parametri da passare a una funzione e desideri un valore in cambio. Qui sto passando "12345" come argomento a una funzione e dopo aver elaborato la restituzione della variabile XYZ che verrà assegnata a VALUE

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


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

Produzione:

something12345
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.