Valeur de retour dans une fonction de Bash

je travaille avec un script bash et je veux exécuter une fonction pour imprimer une valeur de retour:

function fun1(){
  return 34
}
function fun2(){
  local res=$(fun1)
  echo $res
}

quand j'exécute fun2 , il n'imprime pas"34". Pourquoi est-ce le cas?

178
demandé sur learningbee 2013-06-27 11:17:30

9 réponses

bien que bash ait une déclaration return , la seule chose que vous pouvez spécifier avec elle est le propre statut exit de la fonction (une valeur entre 0 et 255 , 0 signifiant "succès"). Donc return n'est pas ce que vous voulez.

vous pourriez vouloir convertir votre déclaration return en une déclaration echo - de cette façon votre sortie de fonction pourrait être capturée en utilisant des bretelles $() , qui semble être exactement ce que vous voulez.

voici un exemple:

function fun1(){
  echo 34
}

function fun2(){
  local res=$(fun1)
  echo $res
}

une autre façon d'obtenir la valeur de retour (si vous voulez simplement retourner un entier 0-255) est $? .

function fun1(){
  return 34
}

function fun2(){
  fun1
  local res=$?
  echo $res
}

aussi, notez que vous pouvez utiliser la valeur de retour pour utiliser la logique booléenne comme fun1 || fun2 ne lancera fun2 si fun1 renvoie une valeur 0 . La valeur de retour par défaut est la valeur de sortie de la dernière instruction exécutée dans la fonction.

239
répondu tamasgal 2018-04-11 20:09:48

$(...) capture le texte envoyé à stdout par la commande contenue dans. return ne sort pas vers stdout. $? contient le code de résultat de la dernière commande.

fun1 (){
  return 34
}

fun2 (){
  fun1
  local res=$?
  echo $res
}
52
répondu Ignacio Vazquez-Abrams 2013-06-27 07:24:16
Les fonctions

dans Bash ne sont pas des fonctions comme dans les autres langues; ce sont en fait des commandes. Ainsi les fonctions sont utilisées comme si elles étaient des binaires ou des scripts récupérés à partir de votre chemin. Du point de vue de la logique de votre programme, il ne devrait y avoir aucune différence.

Les commandes Shell

sont connectées par des pipes (alias streams), et non par des types de données fondamentales ou définies par l'utilisateur, comme dans les langages de programmation" réels". Il n'y a pas de telle chose comme une valeur de retour pour une commande, peut-être surtout parce qu'il n'y a pas de vrai moyen de le déclarer. Cela peut se produire sur la page de manuel, ou sur la sortie --help de la commande, mais les deux ne sont lisibles que par l'homme et sont donc écrits au vent.

Lorsqu'une commande veut obtenir l'entrée de la lecture à partir de ses flux d'entrée, ou de la liste d'arguments. Dans les deux cas, les chaînes de texte à analyser.

Lorsqu'une commande veut retourner quelque chose, elle doit echo le renvoyer à son flux de sortie. Un autre oftenly practiced way est de stocker la valeur de retour dans des variables globales dédiées. L'écriture dans le flux de sortie est plus claire et plus flexible, parce qu'elle peut prendre aussi des données binaires. Par exemple, vous pouvez retourner un BLOB facilement:

encrypt() {
    gpg -c -o-  # encrypt data in filename to stdout (asks for a passphrase)
}

encrypt public.dat > private.dat # write function result to file

comme d'autres l'ont écrit dans ce thread, l'appelant peut aussi utiliser la substitution de commande $() pour capturer la sortie.

parallèlement, la fonction" retournerait "le code de sortie de gpg (GnuPG). Pensez à la code de sortie comme bonus que d'autres langues n'ont pas, ou, selon votre tempérament, comme un "Schmutzeffekt" de fonctions shell. Ce statut est, par convention, 0 sur le succès ou un entier dans la gamme 1-255 pour autre chose. Pour être clair: return (comme exit ) ne peut prendre une valeur que de 0 à 255, et les valeurs autres que 0 ne sont pas nécessairement des erreurs, comme on le dit souvent.

quand vous ne fournissez pas une valeur explicite avec return le statut est pris à partir de la dernière commande dans un Bash déclaration/fonction/commande et ainsi de suite. Donc il y a toujours un statut, et return est juste un moyen facile de fournir.

32
répondu Andreas Spindler 2013-06-27 08:44:51

l'instruction return définit le code de sortie de la fonction, à peu près comme exit le fera pour le script entier.

le code de sortie de la dernière commande est toujours disponible dans la variable $? .

function fun1(){
  return 34
}

function fun2(){
  local res=$(fun1)
  echo $? # <-- Always echos 0 since the 'local' command passes.

  res=$(fun1)
  echo $?  #<-- Outputs 34
}
20
répondu Austin Phillips 2013-06-27 07:25:15

j'aime faire ce qui suit si l'exécution dans un script où la fonction est définie:

POINTER= # used for function return values

my_function() {
    # do stuff
    POINTER="my_function_return"
}

my_other_function() {
    # do stuff
    POINTER="my_other_function_return"
}

my_function
RESULT="$POINTER"

my_other_function
RESULT="$POINTER"

j'aime cela, parce que je peux alors inclure des déclarations echo dans mes fonctions si je veux

my_function() {
    echo "-> my_function()"
    # do stuff
    POINTER="my_function_return"
    echo "<- my_function. $POINTER"
}
4
répondu doc 2015-07-03 12:02:35

en complément des excellents billets des autres, voici un article résumant ces techniques:

  • définir une variable globale
  • définit une variable globale, dont vous avez passé le nom à la fonction
  • définir le code de retour (et le ramasser avec $?)
  • 'echo' certaines données (et le ramasser avec MYVAR=$(mafonction) )

valeurs retournées de Bash Fonctions

1
répondu Tom Hundt 2018-07-30 21:04:26

le problème avec les autres réponses est qu'ils utilisent soit un global, qui peut être écrasé lorsque plusieurs fonctions sont dans une chaîne d'appel, ou un écho qui signifie que votre fonction ne peut pas produire des informations de diagnostic (vous oublierez votre fonction fait cela et le "résultat", c'est-à-dire la valeur de retour, contiendra plus d'informations que votre interlocuteur s'attend, conduisant à bug bizarre), ou eval qui est très lourd et hacky.

la bonne façon de faire ceci est de mettre la substance de haut niveau dans une fonction et d'utiliser un local avec la règle dynamique de bash. Exemple:

func1() 
{
    ret_val=hi
}

func2()
{
    ret_val=bye
}

func3()
{
    local ret_val=nothing
    echo $ret_val
    func1
    echo $ret_val
    func2
    echo $ret_val
}

func3

Ce sorties

nothing
hi
bye

définition dynamique signifie que ret_val pointe vers un objet différent selon l'appelant! Ceci est différent de la portée lexicale, qui est ce que la plupart des langages de programmation utilisent. C'est en fait une fonctionnalité documentée , facile à manquer, et pas très bien expliqué, voici les docs pour elle (l'emphase est mienne):

les Variables locales à la fonction peuvent être déclarées avec le local builtin. Ces variables ne sont visibles que pour la fonction et le commande il invoque .

pour quelqu'un avec un arrière-plan C/C++/Python/Java/C#/javascript, c'est probablement le plus grand obstacle: les fonctions en bash ne sont pas des fonctions, ce sont des commandes, et se comportent comme telles: elles peuvent sortir en stdout/stderr, elles peut entrer/sortir, ils peuvent retourner un code de sortie. Fondamentalement, il n'y a pas de différence entre définir une commande dans un script et créer un exécutable qui peut être appelé à partir de la commande.

donc au lieu d'écrire votre script comme ceci:

top-level code 
bunch of functions
more top-level code

écrivez comme ceci:

# define your main, containing all top-level code
main() 
bunch of functions
# call main
main  

où main() déclare ret_val local et toutes les autres fonctions renvoient des valeurs via ret_val.

voir aussi https://unix.stackexchange.com/questions/282557/scope-of-local-variables-in-shell-functions .

0
répondu Oliver 2018-08-29 16:26:23

Git Bash sur Windows à l'aide de tableaux de multiple valeurs de retour

CODE BASH:

#!/bin/bash

##A 6-element array used for returning
##values from functions:
declare -a RET_ARR
RET_ARR[0]="A"
RET_ARR[1]="B"
RET_ARR[2]="C"
RET_ARR[3]="D"
RET_ARR[4]="E"
RET_ARR[5]="F"


function FN_MULTIPLE_RETURN_VALUES(){

   ##give the positional arguments/inputs
   ## and  some sensible names:
   local out_dex_1="" ##output index
   local out_dex_2="" ##output index

   ##Echo for debugging:
   echo "running: FN_MULTIPLE_RETURN_VALUES"

   ##Here: Calculate output values:
   local op_var_1="Hello"
   local op_var_2="World"

   ##set the return values:
   RET_ARR[ $out_dex_1 ]=$op_var_1
   RET_ARR[ $out_dex_2 ]=$op_var_2
}


echo "FN_MULTIPLE_RETURN_VALUES EXAMPLES:"
echo "-------------------------------------------"
fn="FN_MULTIPLE_RETURN_VALUES"
out_dex_a=0
out_dex_b=1
eval $fn $out_dex_a $out_dex_b  ##<--Call function
a=${RET_ARR[0]} && echo "RET_ARR[0]: $a "
b=${RET_ARR[1]} && echo "RET_ARR[1]: $b "
echo
##----------------------------------------------##
c="2"
d="3"
FN_MULTIPLE_RETURN_VALUES $c $d ##<--Call function
c_res=${RET_ARR[2]} && echo "RET_ARR[2]: $c_res "
d_res=${RET_ARR[3]} && echo "RET_ARR[3]: $d_res "
echo
##----------------------------------------------##
FN_MULTIPLE_RETURN_VALUES 4 5  ##<---Call function
e=${RET_ARR[4]} && echo "RET_ARR[4]: $e "
f=${RET_ARR[5]} && echo "RET_ARR[5]: $f "
echo
##----------------------------------------------##


read -p "Press Enter To Exit:"

RÉSULTATS ESCOMPTÉS:

FN_MULTIPLE_RETURN_VALUES EXAMPLES:
-------------------------------------------
running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[0]: Hello
RET_ARR[1]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[2]: Hello
RET_ARR[3]: World

running: FN_MULTIPLE_RETURN_VALUES
RET_ARR[4]: Hello
RET_ARR[5]: World

Press Enter To Exit:
-1
répondu J.M.I. MADISON 2017-09-13 15:17:43