Ritorno implicito nelle funzioni bash?


11

Supponiamo che io abbia una funzione bash in questo modo:

gmx(){
  echo "foo";
}

questa funzione restituirà implicitamente il valore di uscita del echocomando o è necessario utilizzare return?

gmx(){
  echo "foo";
  return $?
}

Suppongo che il modo in cui bash funziona, lo stato di uscita del comando finale della funzione bash sia quello che viene "restituito", ma non certo al 100%.

Risposte:


10

returnesegue un ritorno esplicito da una funzione shell o "dot script" (uno script di provenienza). Se returnnon viene eseguito, viene restituito un ritorno implicito alla fine della funzione di shell o del punto script.

Se returnviene eseguito senza un parametro, equivale a restituire lo stato di uscita del comando eseguito più di recente.

Ecco come returnfunziona in tutte le shell POSIX.

Per esempio,

gmx () {
  echo 'foo'
  return "$?"
}

è quindi equivalente a

gmx () {
  echo 'foo'
  return
}

che è lo stesso di

gmx () {
  echo 'foo'
}

In generale, è molto raro che tu debba usarlo $?affatto. È davvero necessario solo se è necessario salvarlo per un uso futuro, ad esempio se è necessario studiarne il valore più volte (nel qual caso si assegnerebbe il valore a una variabile ed eseguirà una serie di test su quella variabile).


2
Un aspetto negativo dell'utilizzo returnè che per funzioni definite come f() (...; cmd; return), impedisce l'ottimizzazione che alcune shell eseguono cmdnello stesso processo della subshell. Con molte shell, ciò significa anche che lo stato di uscita della funzione non porta le informazioni che cmdsono state uccise quando presenti (la maggior parte delle shell non può comunque recuperare tali informazioni).
Stéphane Chazelas,

1
Nota che con pdksh e alcuni dei suoi derivati ​​(come OpenBSD sho posh), avresti bisogno return -- "$?"se ci fosse la possibilità che l'ultimo comando fosse una funzione che ha restituito un numero negativo. mksh(basato anche su pdksh) proibisce alle funzioni di restituire valori negativi.
Stéphane Chazelas,

grazie, aiuta, suppongo di non capire come return xfunzioni diversamente da exit x... l'unica cosa che so è che return xnon uscirà dal processo attuale.
Alexander Mills,

@AlexanderMills Bene, è quello che ho detto: returnè usato per tornare da una funzione o da un punto script. exitfa qualcosa di completamente diverso (termina un processo).
Kusalananda

sì, questo ha senso penso che sto iniziando a capire meglio questo
Alexander Mills,

7

Dalla bash(1)pagina man:

Quando eseguito, lo stato di uscita di una funzione è lo stato di uscita dell'ultimo comando eseguito nel corpo.


giusto, e un corollario potrebbe essere che la dichiarazione di ritorno non è altro che lo stato di uscita?
Alexander Mills,

Immagino returnsia un comando incorporato - sebbene return 1sia diverso da exit 1, ecc. Così
Alexander Mills

1
"return [n]: fa interrompere l'esecuzione di una funzione e restituisce il valore specificato da n al suo chiamante. Se n viene omesso, lo stato di ritorno è quello dell'ultimo comando eseguito nel corpo della funzione." (ibid) Quindi, returnforza lo stato di uscita di una funzione su un valore specifico se specificato.
Ignacio Vazquez-Abrams,

1
@AlexandwrMills Sì, returne exitsono entrambi integrati, tranne che returnpossono essere utilizzati solo all'interno della funzione. Non puoi terminare uno script con return. Lo stato di uscita è il valore restituito dal comando. returnè un comando che restituisce quel valore. Quindi "la dichiarazione di ritorno non è altro che lo stato di uscita" non è del tutto accurata. Uno è un valore, l'altro è comando più valore.
Sergiy Kolodyazhnyy,

1
@AlexanderMills, returnritorna dalla funzione, exitesce dall'intera shell. È esattamente lo stesso di in, diciamo C con returnvs. exit(n), o returnvs. sys.exit()in Python.
ilkkachu,

2

Aggiungerò solo alcune note di cautela alle risposte già fornite:

  • Anche se returnha un significato molto speciale per la shell, dal punto di vista della sintassi, è un comando incorporato della shell e un'istruzione return viene analizzata come qualsiasi altro comando semplice. Quindi, ciò significa che come nell'argomento di qualsiasi altro comando, $?quando non quotato, sarebbe soggetto a split + glob

    Quindi è necessario citare questo $?per evitarlo:

    return "$?"
  • returndi solito non accetta alcuna opzione ( ksh93's accetta il solito --help, --man, --author... però). L'unico argomento che si aspetta (facoltativo) è il codice di ritorno. L'intervallo dei codici di ritorno accettati varia da shell a shell e se qualsiasi valore esterno a 0..255 viene correttamente riflesso $?varia anche da shell a shell. Vedi Codice di uscita predefinito al termine del processo? per dettagli al riguardo.

    La maggior parte delle shell accetta numeri negativi (dopo tutto, l'argomento passato alla chiamata _exit()/ exitgroup()system è un int, quindi con valori che comprendono almeno -2 31 a 2 31 -1, quindi ha senso solo che le shell accettino lo stesso intervallo per le sue funzioni) .

    La maggior parte delle shell usa waitpid()e. API per recuperare che lo stato di uscita tuttavia in questo caso, è troncato a un numero compreso tra 0 e 255 quando immagazzinato in $?. Anche se invocare una funzione non comporta la generazione di un processo e utilizzato waitpid()per recuperare il suo stato di uscita poiché tutto viene eseguito nello stesso processo, molte shell imitano anche quel waitpid()comportamento quando si invocano funzioni. Ciò significa che anche se chiami returncon un valore negativo, $?conterrà un numero positivo.

    Ora, tra quelle shell che returnaccettano numeri negativi (ksh88, ksh93, bash, zsh, pdksh e derivati ​​diversi da mksh, yash), ce ne sono alcuni (pdksh e yash) che ne hanno bisogno scritto return -- -123come altrimenti che -123è preso come tre -1, -2, -3opzioni non valide.

    Dato che pdksh e i suoi derivati ​​(come OpenBSD sho posh) conservano il numero negativo in $?, ciò significa che return "$?"farebbe fallire quando $?contiene un numero negativo (cosa che accadrebbe quando l'ultimo comando eseguito fosse una funzione che restituiva un numero negativo).

    Quindi return -- "$?"sarebbe meglio in quelle conchiglie. Si noti tuttavia che mentre è supportato dalla maggior parte delle shell, quella sintassi non è POSIX e in pratica non è supportata da mkshe derivati ​​di cenere.

    Quindi, per riassumere, con shell basate su pdksh, puoi usare numeri negativi negli argomenti per le funzioni, ma se lo fai, return "$@"non funzionerà. In altre shell return "$@"funzionerà e dovresti evitare di usare numeri negativi (o numeri al di fuori di 0..255) come argomenti per return.

  • In tutte le shell che conosco, la chiamata returndall'interno di una subshell in esecuzione all'interno di una funzione causerà l'uscita della subshell (con lo stato di uscita fornito se presente o quello dell'ultimo comando eseguito), ma non provocherà altrimenti un ritorno dalla funzione ( per me, non è chiaro se POSIX ti offra tale garanzia, alcuni sostengono che exitdovrebbe essere usato al posto delle uscite secondarie all'interno delle funzioni). Per esempio

    f() {
      (return 3)
      echo "still inside f. Exit status: $?"
    }
    f
    echo "f exit status: $?"
    

    produrrà:

    still inside f. Exit status: 3
    f exit status: 0
    

0

Sì, il valore di ritorno implicito di una funzione è lo stato di uscita dell'ultimo comando eseguito . Questo è anche vero in qualsiasi punto di qualsiasi script di shell. In qualsiasi punto della sequenza di esecuzione dello script, lo stato di uscita attuale è lo stato di uscita dell'ultimo comando eseguito. Anche comando eseguito come parte di un'assegnazione di variabile: var=$(exit 34). La differenza con le funzioni è che una funzione potrebbe cambiare lo stato di uscita al termine dell'esecuzione della funzione.

Il modo alternativo di modificare lo "stato di uscita attuale" è quello di avviare una shell secondaria ed uscire con lo stato di uscita necessario:

$ $(exit 34)
$ echo "$?"
34

E sì, l' espansione dello stato di uscita deve essere quotata:

$ IFS='123'
$ $(exit 34)
$ echo $?
4

Un (exit 34)lavoro anche.
Alcuni potrebbero obiettare che dovrebbe essere un costrutto più robusto $(return 34)e che un'uscita dovrebbe "uscire" dallo script in esecuzione. Ma $(return 34)non funziona con nessuna versione di bash. Quindi, non è portatile.

Il modo più sicuro per impostare uno stato di uscita è usarlo come è stato progettato per funzionare, definire e returnda una funzione:

exitstatus(){ return "${1:-"$?"}"; }

Quindi, alla fine di una funzione. è esattamente equivalente a non avere nulla o returnoppure return "$?". La fine di una funzione non deve necessariamente significare "l'ultima riga di codice di una funzione".

#!/bin/sh
exitstatus(){ a="${1:-"$?"}"; return "$a"; }
gmx(){
    if     [ "$1" = "one" ]; then
           printf 'foo ';
           exitstatus 78
           return "$?"
    elif   [ "$1" = "two" ]; then
           printf 'baz ';
           exitstatus 89
           return
    else
           printf 'baz ';
           exitstatus 90
    fi
}  

Stampa:

$ ./script
foo 78
baz 89
baz 90

L'unico uso pratico per "$?"è stampare il suo valore: echo "$?"o memorizzarlo in una variabile (in quanto è un valore effimero e cambiare ad ogni comando eseguito): exitstatus=$?(ricordati di citare la variabile in comandi come export EXITSTATUS="$?".

Nel returncomando, l'intervallo di valori valido è generalmente compreso tra 0 e 255, ma è necessario comprendere che i valori di 126 + nvengono utilizzati da alcune shell per segnalare uno stato di uscita speciale, quindi la raccomandazione generale è di utilizzare 0-125.

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.