Retour implicite dans les fonctions bash?

11

Disons que j'ai une fonction bash comme ceci:

gmx(){
  echo "foo";
}

cette fonction retournera-t-elle implicitement la valeur de sortie de la echocommande, ou l'utilisation de return est-elle nécessaire?

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

Je suppose que de la façon dont bash fonctionne, l'état de sortie de la commande finale de la fonction bash est celui qui est "renvoyé", mais pas sûr à 100%.

Alexander Mills
la source

Réponses:

10

returneffectue un retour explicite à partir d'une fonction shell ou "dot script" (un script source). Si returnn'est pas exécuté, un retour implicite est effectué à la fin de la fonction shell ou du script dot.

Si returnest exécuté sans paramètre, cela revient à renvoyer l'état de sortie de la dernière commande exécutée.

C'est ainsi que cela returnfonctionne dans tous les shells POSIX.

Par exemple,

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

est donc équivalent à

gmx () {
  echo 'foo'
  return
}

ce qui est le même que

gmx () {
  echo 'foo'
}

En général, il est très rare que vous en ayez besoin $?. Il n'est vraiment nécessaire que si vous devez l'enregistrer pour une utilisation future, par exemple si vous devez rechercher sa valeur plusieurs fois (auquel cas vous attribuez sa valeur à une variable et effectuez une série de tests sur cette variable).

Kusalananda
la source
2
Un inconvénient de l'utilisation returnest que pour les fonctions définies comme f() (...; cmd; return), cela empêche l'optimisation que quelques shells font de l'exécution cmddans le même processus que le sous-shell. Avec de nombreux shells, cela signifie également que l'état de sortie de la fonction ne porte pas les informations qui cmdont été tuées quand il l'a fait (la plupart des shells ne peuvent pas récupérer ces informations de toute façon).
Stéphane Chazelas
1
Notez qu'avec pdksh et certains de ses dérivés (comme OpenBSD shou posh), vous auriez besoin return -- "$?"s'il y avait une chance que la dernière commande soit une fonction qui a renvoyé un nombre négatif. mksh(également basé sur pdksh) interdit aux fonctions de renvoyer des valeurs négatives.
Stéphane Chazelas
merci cela aide, je suppose que je ne comprends pas comment return xfonctionne différemment de exit x... la seule chose que je sais, c'est que return xcela ne quittera pas le processus actuel.
Alexander Mills
@AlexanderMills Eh bien, c'est ce que j'ai dit: returnest utilisé pour revenir d'une fonction ou d'un script dot. exitfait quelque chose de complètement différent (termine un processus).
Kusalananda
ouais ça a du sens je pense que je commence à mieux comprendre
Alexander Mills
7

Depuis la bash(1)page de manuel:

Une fois exécutée, l'état de sortie d'une fonction est l'état de sortie de la dernière commande exécutée dans le corps.

Ignacio Vazquez-Abrams
la source
à droite, et un corollaire pourrait être que la déclaration de retour n'est rien de plus que le statut de sortie?
Alexander Mills
Je suppose que returnc'est une commande intégrée - bien qu'elle return 1soit différente de exit 1, etc.
Alexander Mills
1
"return [n]: arrête l'exécution d'une fonction et renvoie la valeur spécifiée par n à son appelant. Si n est omis, l'état de retour est celui de la dernière commande exécutée dans le corps de la fonction." (ibid) Ainsi, returnforce l'état de sortie d'une fonction à une valeur spécifique si elle est spécifiée.
Ignacio Vazquez-Abrams
1
@AlexandwrMills Oui, returnet exitsont tous les deux intégrés, sauf qu'ils returnne peuvent être utilisés que dans la fonction. Vous ne pouvez pas terminer un script avec return. Le statut de sortie est la valeur renvoyée par la commande. returnest une commande qui renvoie cette valeur. Ainsi, "la déclaration de retour n'est rien de plus que le statut de sortie" n'est tout simplement pas tout à fait exacte. L'un est une valeur, l'autre est la commande plus la valeur.
Sergiy Kolodyazhnyy
1
@AlexanderMills, returnrevient de la fonction, exitquitte le shell entier. C'est exactement la même chose qu'en, disons C avec returnvs exit(n), ou returnvs sys.exit()en Python.
ilkkachu
2

J'ajouterai simplement quelques notes de prudence aux réponses déjà fournies:

  • Même si elle returna une signification très spéciale pour le shell, d'un point de vue syntaxique, il s'agit d'une commande intégrée au shell et une instruction de retour est analysée comme toute autre commande simple. Donc, cela signifie que, comme dans l'argument de toute autre commande, $?lorsqu'elle n'est pas citée, elle serait soumise à split + glob

    Vous devez donc citer cela $?pour l'éviter:

    return "$?"
  • returnhabituellement n'accepte aucune option ( ksh93« accepte de l'habituel --help, --man, --author... cependant). Le seul argument qu'il attend (facultatif) est le code retour. La plage des codes de retour acceptés varie d'un shell à l'autre, et si une valeur en dehors de 0..255 est correctement reflétée, $?elle varie également d'un shell à l'autre. Voir Code de sortie par défaut à la fin du processus? pour plus de détails à ce sujet.

    La plupart des shells acceptent des nombres négatifs (après tout, l'argument passé à l' appel _exit()/ exitgroup()system est un int, donc avec des valeurs englobant au moins -2 31 à 2 31 -1, il est donc logique que les shells acceptent la même plage pour ses fonctions) .

    La plupart des obus utilisent le waitpid()et co. API pour récupérer cet état de sortie, mais dans ce cas, il est tronqué à un nombre compris entre 0 et 255 lorsqu'il est stocké dans $?. Même si l'invocation d'une fonction n'implique pas la création d'un processus et permet waitpid()de récupérer son état de sortie car tout se fait dans le même processus, de nombreux shells imitent également ce waitpid()comportement lors de l'appel de fonctions. Ce qui signifie que même si vous appelez returnavec une valeur négative, $?contiendra un nombre positif.

    Maintenant, parmi les shells dont returnles nombres négatifs sont acceptés (ksh88, ksh93, bash, zsh, pdksh et dérivés autres que mksh, yash), il y en a quelques-uns (pdksh et yash) qui ont besoin de l'écrire return -- -123comme sinon, cela -123est pris comme trois -1, -2, -3options invalides.

    Comme pdksh et ses dérivés (comme OpenBSD shou posh) conservent le nombre négatif dans $?, cela signifie que faire return "$?"échouerait lorsqu'il $?contient un nombre négatif (ce qui se produirait lorsque la dernière commande d'exécution était une fonction qui a renvoyé un nombre négatif).

    Ce return -- "$?"serait mieux dans ces coquilles. Cependant, notez que bien qu'elle soit prise en charge par la plupart des shells, cette syntaxe n'est pas POSIX et, en pratique, n'est pas prise en charge par mkshles dérivés ash.

    Donc, pour résumer, avec des shells basés sur pdksh, vous pouvez utiliser des nombres négatifs dans les arguments des fonctions, mais si vous le faites, return "$@"cela ne fonctionnera pas. Dans d'autres shells, return "$@"cela fonctionnera et vous devriez éviter d'utiliser des nombres négatifs (ou des nombres en dehors de 0..255) comme arguments pour return.

  • Dans tous les shells que je connais, appeler returnde l'intérieur d'un sous-shell s'exécutant à l'intérieur d'une fonction entraînera la sortie du sous-shell (avec l'état de sortie fourni le cas échéant ou celui de la dernière commande exécutée), mais ne provoquera pas autrement un retour de la fonction ( pour moi, il est difficile de savoir si POSIX vous donne cette garantie, certains affirment que cela exitdevrait être utilisé à la place des sous-coquilles de sortie à l'intérieur des fonctions). Par exemple

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

    affichera:

    still inside f. Exit status: 3
    f exit status: 0
    
Stéphane Chazelas
la source
0

Oui, la valeur de retour implicite d'une fonction est l'état de sortie de la dernière commande exécutée . Cela est également vrai à tout moment de n'importe quel script shell. À tout moment de la séquence d'exécution du script, l'état de sortie actuel est l'état de sortie de la dernière commande exécutée. Même commande exécutée dans le cadre d'une affectation de variable: var=$(exit 34). La différence avec les fonctions est qu'une fonction peut changer l'état de sortie à la fin de l'exécution de la fonction.

L'autre moyen de modifier le "statut de sortie actuel" consiste à démarrer un sous-shell et à le quitter avec le statut de sortie requis:

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

Et oui, l' extension du statut de sortie doit être citée:

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

Un (exit 34)travail aussi.
Certains peuvent faire valoir qu'une construction plus robuste devrait être $(return 34), et qu'une sortie devrait "quitter" le script en cours d'exécution. Mais $(return 34)ne fonctionne avec aucune version de bash. Donc, ce n'est pas portable.

Le moyen le plus sûr de définir un statut de sortie est de l'utiliser tel qu'il a été conçu pour fonctionner, définir et à returnpartir d'une fonction:

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

Donc, à la fin d'une fonction. il est exactement équivalent de n'avoir rien ou returnou return "$?". La fin d'une fonction n'a pas besoin de signifier la "dernière ligne de code d'une fonction".

#!/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
}  

Imprime:

$ ./script
foo 78
baz 89
baz 90

La seule utilisation pratique de "$?"est d'imprimer sa valeur: echo "$?"ou de la stocker dans une variable (car c'est une valeur éphémère et de la changer à chaque commande exécutée): exitstatus=$?(n'oubliez pas de citer la variable dans des commandes comme export EXITSTATUS="$?".

Dans la returncommande, la plage de valeurs valide est généralement comprise entre 0 et 255, mais sachez que les valeurs de 126 + nsont utilisées par certains shells pour signaler un état de sortie spécial.Par conséquent, la recommandation générale consiste à utiliser 0-125.

Isaac
la source