Mengembalikan nilai dari fungsi yang dipanggil dalam skrip shell

126

Saya ingin mengembalikan nilai dari fungsi yang disebut dalam skrip shell. Mungkin saya kehilangan sintaks. Saya mencoba menggunakan variabel global. Tapi itu juga tidak berhasil. Kodenya adalah:

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
Mridul Vishal
sumber
Tidak terkait dengan pertanyaan Anda, tapi bagaimanapun ... jika Anda mencoba untuk mendapatkan kunci, Anda dapat menggunakan perintah "lockfile".
Víctor Herraiz

Jawaban:

277

Fungsi Bash tidak dapat mengembalikan string secara langsung seperti yang Anda inginkan. Anda dapat melakukan tiga hal:

  1. Gema sebuah string
  2. Kembalikan status keluar, yaitu angka, bukan string
  3. Bagikan variabel

Ini juga berlaku untuk beberapa cangkang lainnya.

Berikut cara melakukan masing-masing opsi tersebut:

1. Senar gema

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. Kembali status keluar

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. Bagikan variabel

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
olibre
sumber
2
Jangan gunakan functionkata kunci untuk mendefinisikan fungsi bash. Itu akan membuatnya kurang portabel. Menghapusnya.
dimir
2
Dalam contoh ketiga Anda, retval bukanlah variabel lingkungan. Ini hanyalah variabel shell. Ini hanya akan menjadi variabel lingkungan jika Anda mengekspornya. Mungkin judul contoh ketiga harus "variabel global" bukan "variabel lingkungan".
William Pursell
4
Dalam contoh kedua, daripada menetapkan dari $ ?, lebih idiomatis untuk menulis "if testlock; then ..."
William Pursell
@WilliamPursell Saya telah menghapus kata 'lingkungan' yang salah. Mari kita simpan "$?" untuk tujuan pedagogis. Saya telah mengaktifkan komunitas Wiki, jadi Anda semua bebas untuk meningkatkan jawabannya ;-)
olibre
1
@ManuelJordan, Functions hanya dapat mengembalikan kode keluar dan> & 2 log ke stderror, jadi, gema terakhir ditulis ke stdout, jadi, fungsi panggilan HANYA menangkap stdout dan bukan stderr. Dengan asumsi eksekusi adalah single thread, opsi yang lebih baik adalah mempertahankan variabel khusus yang spesifik seperti metode luar TEST_LOCK_STATUS = "" yang dapat digunakan siapa pun setelah memanggil testlock dan menyetel ulang setiap kali pada awal metode
kisna
16

Anda bekerja terlalu keras. Seluruh skrip Anda harus:

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

tapi bahkan itu mungkin terlalu bertele-tele. Saya akan mengkodekannya:

mkdir "$lockdir" || exit 1

tetapi pesan kesalahan yang dihasilkan agak tidak jelas.

William Pursell
sumber
1
Pesan kesalahan yang hilang cukup mudah diperbaiki, meskipun sedikit lebih bertele-tele: mkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; }(perhatikan ;sebelum kurung kurawal tutup). Juga, saya sering mendefinisikan fungsi gagal yang mengambil parameter pesan opsional yang dicetak ke stderr dan kemudian keluar dengan kode pengembalian 1, memungkinkan saya untuk menggunakan yang lebih mudah dibaca mkdir "$lockdir" || fail "could not create lock dir".
blubberdiblub
@blubberdiblub: tetapi fungsi gagal tidak dapat keluar dari fungsi atau skrip "saat ini", bukan? jadi Anda harus menggunakan cmd || fail "error msg" || return 1jika Anda ingin melakukan itu, bukan?
Maks
@Max bukan fungsi saat ini, itu benar. Tetapi itu akan keluar dari skrip saat ini, selama Anda menyebutnya sebagai perintah dan bukan sumbernya . Saya biasanya menganggap failfungsi seperti itu hanya digunakan untuk situasi fatal.
blubberdiblub
12

Jika itu hanya tes benar / salah, miliki fungsi Anda return 0untuk sukses, dan return 1untuk kegagalan. Tesnya kemudian adalah:

if function_name; then
  do something
else
  error condition
fi
glenn jackman
sumber
Persis apa yang saya cari.
Samuel
Apakah ada cara untuk menggunakan notasi ini juga untuk fungsi berparameter?
Alex
@ alex dapatkah Anda memberikan contoh apa yang Anda maksud dengan "fungsi berparameter"?
glenn jackman
'myCopyFunc $ {SOURCE} $ {DEST}', mengembalikan 0 jika berhasil. Misalnya seperti dalam masalah ini: stackoverflow.com/questions/6212219/…
Alex
Ya, tidak apa-apa
glenn jackman
2

Saya pikir mengembalikan 0 untuk succ / 1 untuk gagal (glenn jackman) dan jawaban olibre yang jelas dan jelas mengatakan semuanya; hanya untuk menyebutkan semacam pendekatan "kombo" untuk kasus di mana hasilnya bukan biner dan Anda lebih suka menyetel variabel daripada "menggemakan" hasil (misalnya jika fungsi Anda JUGA misalkan menggemakan sesuatu, pendekatan ini akan tidak bekerja). Lalu bagaimana? (di bawah ini adalah Bourne Shell)

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

seperti di (ya, contohnya agak konyol, itu hanya .. contoh)

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
Ola Aronsson
sumber
1

Jika Anda memiliki beberapa parameter untuk diteruskan ke suatu fungsi dan menginginkan nilai sebagai gantinya. Di sini saya meneruskan "12345" sebagai argumen ke suatu fungsi dan setelah memproses mengembalikan variabel XYZ yang akan ditetapkan ke VALUE

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


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

Keluaran:

something12345
Rishi Bansal
sumber