Valor de retorno em uma function Bash

Eu estou trabalhando com um script bash e quero executar uma function para imprimir um valor de retorno:

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

Quando executo o fun2 , ele não imprime “34”. Por que esse é o caso?

Embora o bash tenha uma instrução de return , a única coisa que você pode especificar com ele é o próprio status de exit da function (um valor entre 0 e 255 , significando “sucesso”). Então, o return não é o que você quer.

Você pode querer converter sua instrução de return em uma instrução echo – assim, a saída da function pode ser capturada usando chaves $() , que parecem ser exatamente o que você deseja.

Aqui está um exemplo:

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

Outra maneira de obter o valor de retorno (se você quiser apenas retornar um inteiro 0-255) é $? .

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

Além disso, observe que você pode usar o valor de retorno para usar lógica booleana como fun1 || fun2 fun1 || fun2 só executará o fun2 se o fun1 retornar um valor 0 . O valor de retorno padrão é o valor de saída da última instrução executada dentro da function.

$(...) captura o texto enviado para stdout pelo comando contido em. return não sai para stdout. $? contém o código do resultado do último comando.

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

Funções no Bash não são funções como em outra linguagem; eles são realmente comandos. Assim, as funções são usadas como se fossem binários ou scripts obtidos de seu caminho. Do ponto de vista da lógica do seu programa, não deve haver diferença.

Os comandos shell são conectados por pipes (também conhecidos como streams) e não por tipos de dados fundamentais ou definidos pelo usuário, como nas linguagens de programação “reais”. Não existe um valor de retorno para um comando, talvez principalmente porque não há uma maneira real de declará-lo. Isso pode ocorrer na página do manual ou na saída --help do comando, mas ambos são apenas legíveis por humanos e, portanto, são escritos ao vento.

Quando um comando deseja obter uma input, ele o lê a partir de seu stream de input ou da lista de argumentos. Em ambos os casos, as cadeias de texto devem ser analisadas.

Quando um comando deseja retornar algo, ele precisa fazer o echo para seu stream de saída. Outra maneira freqüentemente praticada é armazenar o valor de retorno em variables ​​globais dedicadas. Escrever no stream de saída é mais claro e mais flexível, porque pode levar também dados binários. Por exemplo, você pode retornar um BLOB facilmente:

 encrypt() { gpg -c -o- $1 # encrypt data in filename to stdout (asks for a passphrase) } encrypt public.dat > private.dat # write function result to file 

Como outros escreveram neste tópico, o chamador também pode usar a substituição de comando $() para capturar a saída.

Paralelamente, a function “retornaria” o código de saída do gpg (GnuPG). Pense no código de saída como um bônus que outras linguagens não têm, ou, dependendo do seu temperamento, como um “Schmutzeffekt” das funções do shell. Este status é, por convenção, 0 em sucesso ou um inteiro no intervalo 1-255 para outra coisa. Para deixar isto claro: return (como exit ) só pode ter um valor de 0-255, e valores diferentes de 0 não são necessariamente erros, como é frequentemente afirmado.

Quando você não fornece um valor explícito com return o status é obtido do último comando em uma declaração / function / comando do Bash e assim por diante. Portanto, há sempre um status e o return é apenas uma maneira fácil de fornecê-lo.

A instrução de return define o código de saída da function, da mesma forma que a exit fará para o script inteiro.

O código de saída do último comando está sempre disponível no $? variável.

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

Eu gosto de fazer o seguinte se estiver executando em um script onde a function está definida:

 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" 

Eu gosto disso, porque eu posso include instruções de eco em minhas funções se eu quiser

 my_function() { echo "-> my_function()" # do stuff POINTER="my_function_return" echo "<- my_function. $POINTER" } 

Como um complemento aos excelentes posts de outros, aqui está um artigo resumindo estas técnicas:

  • definir uma variável global
  • definir uma variável global, cujo nome você passou para a function
  • definir o código de retorno (e pegá-lo com $?)
  • ‘echo’ alguns dados (e pegar com MYVAR = $ (myfunction))

Retornando Valores das Funções Bash

O problema com outras respostas é que eles usam um global, que pode ser sobrescrito quando várias funções estão em uma cadeia de chamada ou echo, o que significa que sua function não pode gerar informações de diagnóstico (você esquecerá que sua function faz isso e o “resultado”, valor de retorno, irá conter mais informações do que o seu chamador espera, levando a bug estranho), ou eval que é muito pesado e hacky.

A maneira correta de fazer isso é colocar o material de nível superior em uma function e usar um local com a regra de escopo dynamic do bash. Exemplo:

 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 

Esta saídas

 nothing hi bye 

O escopo dynamic significa que o ret_val aponta para um object diferente, dependendo do chamador! Isso é diferente do escopo léxico, que é o que a maioria das linguagens de programação usa. Este é realmente um recurso documentado , fácil de perder, e não muito bem explicado, aqui estão os documentos para ele (a ênfase é minha):

Variáveis ​​locais para a function podem ser declaradas com o builtin local. Essas variables ​​são visíveis apenas para a function e os comandos que ela chama .

Para alguém com um background em C / C ++ / Python / Java / C # / javascript, este é provavelmente o maior obstáculo: funções no bash não são funções, elas são comandos e se comportam como tal: elas podem gerar saída para stdout / stderr, elas podem pipe in / out, eles podem retornar um código de saída. Basicamente, não há diferença entre definir um comando em um script e criar um executável que possa ser chamado pelo comando.

Então, ao invés de escrever seu script assim:

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

escreva assim:

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

onde main () declara ret_val como local e todas as outras funções retornam valores via ret_val.

Consulte também https://unix.stackexchange.com/questions/282557/scope-of-local-variables-in-shell-functions .

Git Bash no Windows usando matrizes para vários valores de retorno

CÓDIGO DE 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 ##$1 and $2 some sensible names: local out_dex_1="$1" ##output index local out_dex_2="$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:" 

SAÍDA ESPERADA:

 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: