Pengembalian implisit dalam fungsi bash?

11

Katakanlah saya memiliki fungsi bash seperti:

gmx(){
  echo "foo";
}

Apakah fungsi ini secara implisit mengembalikan nilai keluar dari echoperintah, atau menggunakan kembali diperlukan?

gmx(){
  echo "foo";
  return $?
}

Saya berasumsi bahwa cara bash bekerja, status keluar dari perintah akhir dari fungsi bash adalah yang mendapatkan "dikembalikan", tetapi tidak 100% pasti.

Alexander Mills
sumber

Jawaban:

10

returnmelakukan pengembalian eksplisit dari fungsi shell atau "dot script" (skrip bersumber). Jika returntidak dieksekusi, pengembalian implisit dibuat di akhir fungsi shell atau skrip dot.

Jika returndijalankan tanpa parameter, itu sama dengan mengembalikan status keluar dari perintah yang paling baru dieksekusi.

Begitulah cara returnkerjanya di semua shell POSIX.

Sebagai contoh,

gmx () {
  echo 'foo'
  return "$?"
}

Oleh karena itu setara dengan

gmx () {
  echo 'foo'
  return
}

yang sama dengan

gmx () {
  echo 'foo'
}

Secara umum, sangat jarang Anda perlu menggunakannya $?sama sekali. Ini benar-benar hanya diperlukan jika Anda perlu menyimpannya untuk penggunaan di masa mendatang, misalnya jika Anda perlu menyelidiki nilainya beberapa kali (dalam hal ini Anda akan menetapkan nilainya ke suatu variabel dan melakukan serangkaian tes pada variabel itu).

Kusalananda
sumber
2
Salah satu kelemahan menggunakan returnadalah untuk fungsi-fungsi yang didefinisikan seperti f() (...; cmd; return), ini mencegah optimasi yang dilakukan oleh beberapa shell dalam menjalankan cmdproses yang sama dengan subkulit. Dengan banyak shell, itu juga berarti bahwa status keluar dari fungsi tidak membawa informasi yang cmdtelah dimatikan ketika itu terjadi (kebanyakan shell tidak dapat mengambil informasi itu pula).
Stéphane Chazelas
1
Perhatikan bahwa dengan pdksh dan beberapa turunannya (seperti OpenBSD shatau posh), Anda perlu return -- "$?"jika ada kemungkinan bahwa perintah terakhir adalah fungsi yang mengembalikan angka negatif. mksh(juga berdasarkan pdksh) melarang fungsi mengembalikan nilai negatif.
Stéphane Chazelas
terima kasih ini membantu, saya kira saya tidak mengerti bagaimana return xfungsinya berbeda dari exit x... satu-satunya hal yang saya tahu adalah return xtidak akan keluar dari proses saat ini.
Alexander Mills
@AlexanderMills Yah, itulah yang saya katakan: returndigunakan untuk kembali dari fungsi atau skrip titik. exitmelakukan sesuatu yang sama sekali berbeda (mengakhiri suatu proses).
Kusalananda
ya itu masuk akal saya pikir saya mulai mendapatkan penanganan yang lebih baik tentang ini
Alexander Mills
7

Dari bash(1)halaman manual:

Ketika dieksekusi, status keluar dari suatu fungsi adalah status keluar dari perintah terakhir yang dijalankan dalam tubuh.

Ignacio Vazquez-Abrams
sumber
benar, dan akibat wajar mungkin bahwa pernyataan pengembalian tidak lebih dari status keluar?
Alexander Mills
Saya kira itu returnadalah perintah builtin - walaupun return 1berbeda dari exit 1, dan sebagainya
Alexander Mills
1
"return [n]: Menyebabkan suatu fungsi untuk berhenti mengeksekusi dan mengembalikan nilai yang ditentukan oleh n ke pemanggilnya. Jika n dihilangkan, status pengembalian adalah perintah terakhir yang dijalankan di badan fungsi." (ibid) Jadi, returnmemaksa status keluar dari suatu fungsi ke nilai tertentu jika ditentukan.
Ignacio Vazquez-Abrams
1
@AlexandwrMills Ya, returndan exitkeduanya merupakan bawaan, kecuali returnhanya dapat digunakan di dalam fungsi. Anda tidak dapat mengakhiri skrip dengan return. Status keluar adalah nilai yang dikembalikan oleh perintah. returnadalah perintah yang mengembalikan nilai itu. Jadi "pernyataan pengembalian tidak lebih dari status keluar" tidak cukup akurat. Satu adalah nilai, yang lainnya adalah nilai plus perintah.
Sergiy Kolodyazhnyy
1
@AlexanderMills, returnkembali dari fungsi, exitkeluar dari seluruh shell. Persis sama dengan di, katakanlah C dengan returnvs. exit(n), atau returnvs. sys.exit()dengan Python.
ilkkachu
2

Saya hanya akan menambahkan beberapa catatan peringatan untuk jawaban yang sudah disediakan:

  • Meskipun returnmemiliki arti yang sangat khusus untuk shell, dari sudut pandang sintaks, itu adalah perintah builtin shell dan pernyataan kembali diurai seperti perintah sederhana lainnya. Jadi, itu berarti bahwa seperti dalam argumen dari perintah lain, $?ketika tidak dikutip, akan dikenakan split + glob

    Jadi, Anda perlu mengutipnya $?untuk menghindarinya:

    return "$?"
  • returnbiasanya tidak menerima pilihan apapun ( ksh93's menerima biasa --help, --man, --author... meskipun). Satu-satunya argumen yang diharapkan (opsional) adalah kode kembali. Kisaran kode pengembalian yang diterima bervariasi dari shell ke shell, dan apakah nilai apa pun di luar 0..255 tercermin dengan baik $?juga bervariasi dari shell ke shell. Lihat Kode keluar default saat proses dihentikan? untuk detailnya.

    Kebanyakan shell menerima angka negatif (setelah semua, argumen yang diteruskan ke _exit()/ exitgroup()system call adalah int, jadi dengan nilai yang mencakup setidaknya -2 31 hingga 2 31 -1, jadi masuk akal jika shell menerima rentang yang sama untuk fungsinya) .

    Kebanyakan shell menggunakan waitpid()dan co. API untuk mengambil bahwa status exit Namun dalam hal ini, itu dipotong untuk angka antara 0 dan 255 ketika disimpan di $?. Meskipun memohon suatu fungsi tidak melibatkan proses pemijahan dan digunakan waitpid()untuk mengambil status keluarnya karena semua dilakukan dalam proses yang sama, banyak shell juga meniru waitpid()perilaku itu ketika menjalankan fungsi. Yang berarti bahwa bahkan jika Anda menelepon returndengan nilai negatif, $?akan berisi angka positif.

    Sekarang, di antara cangkang yang returnmenerima angka negatif (ksh88, ksh93, bash, zsh, pdksh, dan turunannya selain mksh, yash), ada beberapa (pdksh dan yash) yang memerlukannya ditulis return -- -123seperti jika -123tidak diambil sebagai tiga -1, -2, -3opsi tidak valid.

    Karena pdksh dan turunannya (seperti OpenBSD shatau posh) menyimpan angka negatif $?, itu berarti bahwa melakukan return "$?"akan gagal ketika $?berisi angka negatif (yang akan terjadi ketika perintah run terakhir adalah fungsi yang mengembalikan angka negatif).

    Jadi return -- "$?"akan lebih baik di kerang itu. Namun perhatikan bahwa walaupun didukung oleh sebagian besar shell, sintaks itu bukan POSIX dan dalam praktiknya tidak didukung oleh mkshdan turunan abu.

    Jadi, untuk meringkas, dengan shell berbasis pdksh, Anda dapat menggunakan angka negatif dalam argumen untuk fungsi, tetapi jika Anda melakukannya, return "$@"tidak akan berfungsi. Di shell lain, return "$@"akan berfungsi dan Anda harus menghindari menggunakan angka negatif (atau angka di luar 0..255) sebagai argumen return.

  • Di semua shell yang saya tahu, memanggil returndari dalam subkulit yang berjalan di dalam suatu fungsi akan menyebabkan subkulit keluar (dengan status keluar yang disediakan jika ada atau yang ada pada perintah terakhir dijalankan), tetapi tidak akan menyebabkan pengembalian dari fungsi ( bagi saya, tidak jelas apakah POSIX memberi Anda garansi itu, beberapa berpendapat bahwa exitharus digunakan daripada keluar subkulit dalam fungsi). Contohnya

    f() {
      (return 3)
      echo "still inside f. Exit status: $?"
    }
    f
    echo "f exit status: $?"
    

    akan menampilkan:

    still inside f. Exit status: 3
    f exit status: 0
Stéphane Chazelas
sumber
0

Ya, nilai balik implisit dari suatu fungsi adalah status keluar dari perintah yang terakhir dieksekusi . Itu juga berlaku pada setiap titik dari skrip shell apa pun. Kapan pun dalam urutan eksekusi skrip, status keluar saat ini adalah status keluar dari perintah terakhir yang dijalankan. Bahkan perintah dieksekusi sebagai bagian dari tugas variabel: var=$(exit 34). Perbedaannya dengan fungsi adalah bahwa suatu fungsi dapat mengubah status keluar pada akhir pelaksanaan fungsi.

Cara alternatif untuk mengubah "status keluar sekarang" adalah memulai sub shell dan keluar dengan status keluar apa pun yang diperlukan:

$ $(exit 34)
$ echo "$?"
34

Dan ya, perluasan status keluar memang perlu dikutip:

$ IFS='123'
$ $(exit 34)
$ echo $?
4

A (exit 34)juga bekerja.
Beberapa mungkin berpendapat bahwa konstruk yang lebih kuat seharusnya $(return 34), dan bahwa jalan keluar harus "keluar" dari skrip yang sedang dieksekusi. Tetapi $(return 34)tidak berfungsi dengan versi bash apa pun . Jadi, ini tidak portabel.

Cara teraman untuk menetapkan status keluar adalah menggunakannya seperti yang dirancang untuk bekerja, menentukan, dan returndari suatu fungsi:

exitstatus(){ return "${1:-"$?"}"; }

Jadi, di akhir fungsi. itu persis sama dengan tidak memiliki apa pun returnatau return "$?". Akhir dari suatu fungsi tidak perlu berarti "baris kode terakhir dari suatu fungsi".

#!/bin/sh
exitstatus(){ a="${1:-"$?"}"; return "$a"; }
gmx(){
    if     [ "$1" = "one" ]; then
           printf 'foo ';
           exitstatus 78
           return "$?"
    elif   [ "$1" = "two" ]; then
           printf 'baz ';
           exitstatus 89
           return
    else
           printf 'baz ';
           exitstatus 90
    fi
}  

Akan dicetak:

$ ./script
foo 78
baz 89
baz 90

Satu-satunya penggunaan praktis "$?"adalah untuk mencetak nilainya: echo "$?"atau untuk menyimpannya dalam variabel (karena merupakan nilai sementara dan berubah dengan setiap perintah yang dijalankan): exitstatus=$?(ingat untuk mengutip variabel dalam perintah seperti export EXITSTATUS="$?".

Dalam returnperintah, rentang nilai yang valid umumnya 0 hingga 255, tetapi pahami bahwa nilai-nilai 126 + ndigunakan oleh beberapa shell untuk memberi sinyal status keluar khusus, jadi, rekomendasi umum adalah menggunakan 0-125.

Ishak
sumber