Bash işlevinde dönüş değeri


306

Bir bash betiği ile çalışıyorum ve bir dönüş değeri yazdırmak için bir işlev yürütmek istiyorum:

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

Çalıştırdığımda fun2"34" yazdırmıyor. Neden böyle?


8
returnsizin durumunuz esasen exit codehangi aralık ile aynıdır 0 - 255. echo@Septi tarafından önerildiği gibi kullanın . Çıkış kodları ile yakalanabilir $?.
devnull

1
Bu durumda yankıyı eğlencede kullanmak çok daha esnektir1. Bu unix-programlama fikri: echo sonuçları standart çıktıya gönderir ve daha sonra res = $ (fun1) ile diğer işlevler tarafından tekrar kullanılabilir veya doğrudan diğer işlevlere bağlanabilir:function a() { echo 34; } function b() { while read data; do echo $data ; done ;} a | b
Arne Babenhauserheide

Bunu yapmanın doğru yolu, en üst düzey şeyleri bir işleve koymak ve bash'ın dinamik kapsam belirleme kuralına sahip bir yerel kullanmaktır. Göstermek için bir cevap oluşturacağım, bu iyi bilinen bir özellik değil, tam olarak desteklenen bir özellik.
Oliver


Yanıtlar:


375

Bash bir returndeyime sahip olsa da, onunla belirtebileceğiniz tek şey işlevin kendi exitdurumudur ( 0ve 255, 0 arasında bir değer "başarı" anlamına gelir). Yani returnistediğiniz şey değil.

İfadenizi returnbir echoifadeye dönüştürmek isteyebilirsiniz - bu şekilde işlev çıktısı $()tam olarak istediğiniz gibi görünen parantez kullanılarak yakalanabilir .

İşte bir örnek:

function fun1(){
  echo 34
}

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

Dönüş değerini almanın başka bir yolu (sadece 0-255 tamsayısını döndürmek istiyorsanız) $?.

function fun1(){
  return 34
}

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

Ayrıca, not sizin gibi boole mantığı kullanmak dönüş değeri kullanabileceği fun1 || fun2sadece çalışacak fun2eğer fun1getiri bir 0değer. Varsayılan dönüş değeri, işlev içinde yürütülen son ifadenin çıkış değeridir.


2
Yürütmeniz gerekir fun1ve daha sonra dönüş değeri saklanır $?. Her ne kadar bunu tavsiye etmem ...
tamasgal

9
Neden kullanmıyorsunuz $??
Pithikos

147
Hayır, lanet olası dönüş değerine ihtiyacım var . Yankı ile cehenneme.
Tomáš Zato - Monica'yı eski durumuna getir

7
Bu ortamda @Buuhirn, bu ||yapı ile 0 çıkış kodu başarı olarak kabul edilir ve bu nedenle "doğru" olarak kabul edilir. Sıfırdan farklı hata ve dolayısıyla yanlıştır. fun1 || fun2Gerçek dönüş değerlerinin kendisindeki bir işleç yerine "fun1 başarılı olursa veya fun2 başarılı olursa" kısayolunu düşünün .
Davida

6
Can sıkıcı olan şey, veri sağlaması gereken bir fonksiyonun stdout'a başka şeyler de ekleyemeyeceğidir, çünkü $ () kullanan arayan da bunu alır ve kafası karışır veya çıktıyı ayrıştırmak zorunda kalır. Global değişkenler harika değildir, çünkü aynı global değişkeni iç içe yerleştirilen iki yerde kullanmanız ve verilerin kaybolması sadece bir zaman meselesidir. Verileri yazdırmak veya verileri geri göndermek için ayrı kanallar olmalıdır.
Oliver

68

$(...)içindeki komutla stdout'a gönderilen metni yakalar. returnstdout'a çıkış vermez. $?son komutun sonuç kodunu içerir.

fun1 (){
  return 34
}

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

6
Evet returnayarı için kullanılır $?olduğunu exit status. Yukarıdaki örnekte fun1's exit statusolacaktır 34. Ayrıca, $(...)belirtilen komuttan stdout'a ek olarak stderr'i de yakaladığını unutmayın .
swoop81

59

Bash'deki işlevler diğer dillerdeki işlevler değildir; onlar aslında emirdir. Yani işlevler, yolunuzdan getirilen ikili dosyalar veya komut dosyaları gibi kullanılır. Program mantığınızın bakış açısından gerçekten hiçbir fark olmamalıdır.

Kabuk komutları "gerçek" programlama dillerinde olduğu gibi temel veya kullanıcı tanımlı veri türleriyle değil, borularla (akışlar olarak da bilinir) bağlanır. Bir komutun dönüş değeri gibi bir şey yoktur, belki de çoğunlukla onu bildirmenin gerçek bir yolu olmadığı için. Man sayfasında veya --helpkomutun çıktısında ortaya çıkabilir , ancak her ikisi de sadece insan tarafından okunabilir ve dolayısıyla rüzgara yazılır.

Bir komut girdi almak istediğinde, onu girdi akışından veya bağımsız değişken listesinden okur. Her iki durumda da metin dizelerinin ayrıştırılması gerekir.

Bir komut bir şeyi geri döndürmek istediğinde echo, onun çıkış akışına girmesi gerekir. Sıklıkla uygulanan bir başka yol da, dönüş değerini tahsis edilmiş küresel değişkenlerde saklamaktır. Çıkış akışına yazmak daha net ve daha esnektir, çünkü ikili verileri de alabilir. Örneğin, kolayca bir BLOB döndürebilirsiniz:

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

Diğerleri bu iş parçacığında yazdığı gibi, arayan da $()çıktıyı yakalamak için komut ikamesini kullanabilir .

Paralel olarak, fonksiyon gpg(GnuPG) çıkış kodunu "döndürür" . Çıkış kodunu diğer dillerin sahip olmadığı veya mizacınıza bağlı olarak kabuk işlevlerinin bir "Schmutzeffekt" değeri olarak düşünün. Bu durum, kural olarak, başarı için 0 veya başka bir şey için 1-255 aralığında bir tamsayıdır. Bunu açıklığa kavuşturmak için: return(gibi exit) yalnızca 0-255 arasında bir değer alabilir ve 0 dışındaki değerler genellikle iddia edildiği gibi hatalar olmak zorunda değildir.

returnDurum ile açık bir değer sağlamadığınızda, Bash deyimi / işlevi / komutundaki son komuttan alınır vb. Yani her zaman bir durum vardır ve returnbunu sağlamanın kolay bir yoludur.


4
Komutlara karşı fonksiyonların açıklanması ve bunun arayan kişiye veri gönderme kavramını nasıl etkilediğini açıklayan +1
Oliver

4
Kabuk programlamanın komutları borular yoluyla bağlamakla ilgili olduğunu açıklayan +1. Diğer programlama dilleri dönüş türleri ile fonksiyonlar oluşturur. Bash, metin akışları aracılığıyla komutlar oluşturur.
jrahhali

29

returnİfadesi olduğu kadar aynı işlevin çıkış kodu, ayarlar exittüm komut dosyası için yapacağız.

Son komutun çıkış kodu her zaman $?değişkende bulunur.

function fun1(){
  return 34
}

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

  res=$(fun1)
  echo $?  #<-- Outputs 34
}

21

Diğer cevaplarla ilgili sorun, ya bir çağrı zincirinde birkaç fonksiyon olduğunda üzerine yazılabilen bir global kullanmaları ya echoda fonksiyonunuzun teşhis bilgisi veremediği anlamına gelir (fonksiyonunuzun bunu ve "sonucu", yani geri döndüğünü unutursunuz değeri, arayanın beklediğinden daha fazla bilgi içerecek, garip hataya yol açacak) veya evalçok ağır ve hacky.

Bunu yapmanın uygun yolu, en üst düzey şeyleri bir işleve koymak ve localbash'ın dinamik kapsamlandırma kuralını kullanmaktır. Misal:

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

Bu çıktılar

nothing
hi
bye

Dinamik kapsam belirleme, ret_val , arayana bağlı olarak farklı bir nesneyi gösterir! Bu, programlama dillerinin çoğunun kullandığı sözcük kapsamından farklıdır. Bu aslında belgelendirilmiş bir özelliktir , kaçırması kolaydır ve çok iyi açıklanmamıştır, işte bunun için belgeler (vurgu benimdir):

İşleve yerel değişkenler yerel yerleşik ile bildirilebilir. Bu değişkenler sadece fonksiyon tarafından görülebilir ve çağırdığı komutları.

Bir C / C ++ / Python / Java / C # / javascript deneyimi olan birini, bu büyük engel muhtemelen: bash fonksiyonları onlar komutlar vardır ve gibi davranırlar, fonksiyonlar değildir: bunlar çıktı için can stdout/ stderronlar boru içinde can / out, bir çıkış kodu döndürebilir. Temel olarak bir komut dosyasında bir komut tanımlamak ve komut satırından çağrılabilen bir yürütülebilir dosya oluşturmak arasında bir fark yoktur.

Yani betiğinizi böyle yazmak yerine:

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

şöyle yaz:

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

nerede main()beyan ret_valolaraklocal ve diğer tüm fonksiyonları ile değerleri döndürür ret_val.

Aşağıdaki Unix ve Linux sorusuna da bakın: Kabuk İşlevlerindeki Yerel Değişkenlerin Kapsamı .

Bir diğeri, duruma göre belki de daha iyi bir çözüm, biri ya.teck tarafından gönderildi hangi kullanır local -n.


18

Bunu başarmanın başka bir yolu da ad referanslarıdır (Bash 4.3+ gerektirir).

function example {
  local -n VAR=$1
  VAR=foo
}

example RESULT
echo $RESULT

3
ne yaptığını merak eden herkes -n <name>=<reference>: yeni oluşturulan değişkeni işaret ettiği başka bir değişkene referans yapar <reference>. Diğer atamalar <name>referans alınan değişken üzerinde gerçekleştirilir.
Valerio

7

İşlev tanımlanmış bir komut dosyasında çalışıyorsa aşağıdakileri yapmak istiyorum:

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"

Bunu beğendim, çünkü istersem işlevlerime yankı ifadeleri ekleyebilirim

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

5

Başkalarının mükemmel gönderilerine ek olarak, bu teknikleri özetleyen bir makale:

  • global değişken belirle
  • adını işleve ilettiğiniz global bir değişken belirleyin
  • dönüş kodunu ayarlayın (ve $ ile alsın mı?)
  • 'echo' bazı verileri (ve MYVAR = $ (işlevim) ile al)

Bash İşlevlerinden Değerleri Döndürme


Makale tüm seçenekleri temiz bir şekilde tartıştığından, bu en iyi cevaptır.
mzimmermann

-2

Git Bash üzerinde Windows'un için diziler kullanılarak çoklu dönüş değerlerine

BAŞ KODU:

#!/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:"

BEKLENEN ÇIKTI:

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:
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.