Bir kabuk betiğinde çağrılan işlevden değer döndürme


126

Kabuk betiğinde çağrılan bir işlevden değeri döndürmek istiyorum. Belki de sözdizimini kaçırıyorum. Global değişkenleri kullanmayı denedim. Ama bu da işe yaramıyor. Kod şu şekildedir:

lockdir="somedir"
test() {
    retval=""

    if mkdir "$lockdir"
        then    # Directory did not exist, but it was created successfully
            echo >&2 "successfully acquired lock: $lockdir"
            retval="true"
        else
            echo >&2 "cannot acquire lock, giving up on $lockdir"
            retval="false"
    fi
    return retval
}


retval=test()
if [ "$retval" == "true" ]
    then
        echo "directory not created"
    else
        echo "directory already created"
fi

Sorunuzla ilgili değil, ama yine de ... bir kilit almaya çalışıyorsanız, "lockfile" komutunu kullanabilirsiniz.
Víctor Herraiz

Yanıtlar:


277

Bash işlevi, istediğiniz gibi bir dizeyi doğrudan döndüremez. Üç şey yapabilirsiniz:

  1. Bir dizeyi yankıla
  2. Dize değil, sayı olan bir çıkış durumu döndür
  3. Değişken paylaşın

Bu, diğer bazı mermiler için de geçerlidir.

İşte bu seçeneklerin her birinin nasıl yapılacağı:

1. Yankı dizeleri

lockdir="somedir"
testlock(){
    retval=""
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval="true"
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval="false"
    fi
    echo "$retval"
}

retval=$( testlock )
if [ "$retval" == "true" ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2. Çıkış durumuna dön

lockdir="somedir"
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
    return "$retval"
}

testlock
retval=$?
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

3. Değişkeni paylaşın

lockdir="somedir"
retval=-1
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
}

testlock
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2
functionBir bash işlevini tanımlamak için bir anahtar sözcük kullanmayın . Bu onu daha az taşınabilir hale getirir. Çıkarma.
dimir

2
Üçüncü örneğinizde, retval bir ortam değişkeni değildir. Bu sadece bir kabuk değişkenidir. Yalnızca dışa aktarırsanız bir ortam değişkeni haline gelecektir. Belki de üçüncü örneğin başlığı "ortam değişkeni" yerine "global değişken" olmalıdır.
William Pursell

4
İkinci örnekte, $? 'Dan atamak yerine "if testlock; then ..." yazmak daha deyimseldir
William Pursell

@WilliamPursell Yanlış 'çevre' kelimesini kaldırdım. "$" Mı kalsın? pedagojik amaç için. Wiki topluluğunu etkinleştirdim, böylece yanıtı geliştirmekte
özgürsünüz

1
@ManuelJordan, Functions yalnızca çıkış kodlarını ve> & 2 günlüklerini stderror'a döndürebilir, bu nedenle son yankı stdout'a yazılır, bu nedenle çağıran işlev stderr değil YALNIZCA stdout'u yakalar. Yürütmenin tek iş parçacıklı olduğunu varsayarsak, daha iyi bir seçenek, herkesin test kilidini çağırdıktan sonra kullanabileceği TEST_LOCK_STATUS = "" dış yöntem gibi özel bir değişkeni korumak ve yöntemin başlangıcında her seferinde sıfırlamaktır
kisna

16

Çok çalışıyorsun. Komut dosyanızın tamamı şöyle olmalıdır:

if mkdir "$lockdir" 2> /dev/null; then 
  echo lock acquired
else
  echo could not acquire lock >&2
fi

ama bu bile muhtemelen çok ayrıntılı. Ben kodlardım:

mkdir "$lockdir" || exit 1

ancak ortaya çıkan hata mesajı biraz belirsizdir.


1
Eksik hata mesajı, biraz daha ayrıntılı olmasına rağmen düzeltmek için yeterince kolaydır: mkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; }( ;kapanış küme ayracının öncesine dikkat edin ). Ayrıca, stderr'e yazdırdığı ve ardından dönüş kodu 1 ile çıkarak daha okunaklı olanı kullanmamı sağlayan isteğe bağlı bir mesaj parametresini alan bir başarısız fonksiyon tanımlıyorum mkdir "$lockdir" || fail "could not create lock dir".
blubberdiblub

@blubberdiblub: ancak başarısız işlevi "mevcut" işlevden veya komut dosyasından çıkamaz, değil mi? yani bunu cmd || fail "error msg" || return 1yapmak istiyorsan kullanmak zorundasın , değil mi?
Maksimum

@Max mevcut işlev değil, bu doğru. Ama siz onu bir komut olarak adlandırdığınız ve kaynak sağlamadığınız sürece mevcut komut dosyasından çıkacaktır . Genelde böyle bir failişlevi yalnızca ölümcül durumlarda kullanıldığı gibi düşünüyorum .
blubberdiblub

12

Yalnızca doğru / yanlış bir test ise, işlevinizi return 0başarı ve return 1başarısızlık için belirleyin. Test şu şekilde olacaktır:

if function_name; then
  do something
else
  error condition
fi

Tam olarak aradığım şey.
Samuel

Parametreli fonksiyonlar için de bu gösterimi kullanmanın bir yolu var mı?
Alex

@alex "parametreleştirilmiş işlev" ile ne demek istediğinize bir örnek verebilir misiniz?
glenn jackman

'myCopyFunc $ {SOURCE} $ {DEST}', başarılı olursa 0 döndürür. Örneğin, bu sayıdaki gibi: stackoverflow.com/questions/6212219/…
Alex

Evet, bu çok iyi
glenn jackman

2

Sanırım başarısızlık için 0 / başarısız için 0 döndürmek (glenn jackman) ve olibre'nin açık ve açıklayıcı cevabı her şeyi söylüyor; sonuçların ikili olmadığı ve bir sonucu "yankılamak" yerine bir değişken ayarlamayı tercih ettiğiniz durumlar için bir tür "birleşik" yaklaşımdan bahsetmek gerekirse (örneğin, işleviniz AYRICA bir şeyi yankıladığını varsayalım, bu yaklaşım çalışmıyor). Sonra ne? (aşağıda Bourne Shell var)

# Syntax _w (wrapReturn)
# arg1 : method to wrap
# arg2 : variable to set
_w(){
eval $1
read $2 <<EOF
$?
EOF
eval $2=\$$2
}

olduğu gibi (evet, örnek biraz saçma, sadece bir .. örnek)

getDay(){
  d=`date '+%d'`
  [ $d -gt 255 ] && echo "Oh no a return value is 0-255!" && BAIL=0 # this will of course never happen, it's just to clarify the nature of returns
  return $d
}

dayzToSalary(){
  daysLeft=0
  if [ $1 -lt 26 ]; then 
      daysLeft=`expr 25 - $1`
  else
     lastDayInMonth=`date -d "`date +%Y%m01` +1 month -1 day" +%d`
     rest=`expr $lastDayInMonth - 25`
     daysLeft=`expr 25 + $rest`
  fi
  echo "Mate, it's another $daysLeft days.."
}

# main
_w getDay DAY # call getDay, save the result in the DAY variable
dayzToSalary $DAY

1

Bir işleve geçmek için bazı parametrelerinizin olması ve karşılığında bir değer istemeniz durumunda. Burada bir işleve argüman olarak "12345" i aktarıyorum ve XYZ değişkenini döndürdükten sonra bu değişkeni VALUE olarak atayacağım.

#!/bin/bash
getValue()
{
    ABC=$1
    XYZ="something"$ABC
    echo $XYZ
}


VALUE=$( getValue "12345" )
echo $VALUE

Çıktı:

something12345
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.