Mengembalikan boolean dari fungsi Bash

211

Saya ingin menulis fungsi bash yang memeriksa apakah file memiliki properti tertentu dan mengembalikan benar atau salah. Maka saya bisa menggunakannya dalam skrip saya di "jika". Tetapi apa yang harus saya kembalikan?

function myfun(){ ... return 0; else return 1; fi;}

maka saya menggunakannya seperti ini:

if myfun filename.txt; then ...

tentu saja ini tidak berhasil. Bagaimana ini bisa dicapai?

luca
sumber
3
turunkan functionkata kunci, myfun() {...}cukup
glenn jackman
2
Yang penting ifadalah status nol-keluar dari myfun: jika myfunkeluar dengan 0, then ...dieksekusi; jika ada else ... yang dieksekusi.
Eelvex
7
@nhed: functionkata kunci adalah bashism, dan akan menyebabkan kesalahan sintaksis pada beberapa shell lainnya. Pada dasarnya, itu tidak perlu atau dilarang, jadi mengapa menggunakannya? Itu bahkan tidak berguna sebagai target grep, karena mungkin tidak ada di sana ( ()bukan grep ).
Gordon Davisson
3
@GordonDavisson: apa? ada kerang lain? ;-)
nhed
Tolong jangan gunakan 0 dan 1. Lihat stackoverflow.com/a/43840545/117471
Bruno Bronosky

Jawaban:

333

Gunakan 0 untuk true dan 1 untuk false.

Sampel:

#!/bin/bash

isdirectory() {
  if [ -d "$1" ]
  then
    # 0 = true
    return 0 
  else
    # 1 = false
    return 1
  fi
}


if isdirectory $1; then echo "is directory"; else echo "nopes"; fi

Edit

Dari komentar @ amichair, ini juga mungkin

isdirectory() {
  if [ -d "$1" ]
  then
    true
  else
    false
  fi
}


isdirectory() {
  [ -d "$1" ]
}
Erik
sumber
4
Tidak, Anda tidak perlu melakukan itu - lihat sampel.
Erik
46
Untuk keterbacaan yang lebih baik Anda dapat menggunakan perintah 'benar' (yang tidak melakukan apa-apa dan berhasil menyelesaikan, yaitu mengembalikan 0) dan perintah 'salah' (yang tidak melakukan apa-apa dan tidak berhasil, yaitu mengembalikan nilai bukan nol). Juga, fungsi yang berakhir tanpa pernyataan pengembalian eksplisit mengembalikan kode keluar dari perintah yang terakhir dieksekusi, jadi dalam contoh di atas, fungsi tubuh dapat dikurangi menjadi hanya [ -d "$1" ].
amichair
24
Namun: masuk akal bila Anda menganggapnya sebagai "kode kesalahan": kode kesalahan 0 = semuanya berjalan ok = 0 kesalahan; kode kesalahan 1 = hal utama yang harus dilakukan panggilan ini gagal; lain: gagal! cari di halaman manual.
domba terbang
7
Masuk akal ketika Anda mempertimbangkan bahwa dalam pemrograman hal-hal biasanya hanya dapat berhasil dalam satu cara, tetapi dapat gagal dalam cara yang tak terbatas. Yah mungkin tidak terbatas, tetapi banyak, kemungkinannya ditumpuk melawan kita. Keberhasilan / Kesalahan bukan boolean. Saya pikir ini "Gunakan 0 untuk benar dan 1 untuk salah." harus membaca "Gunakan 0 untuk sukses dan bukan nol untuk kegagalan".
Davos
6
Tolong jangan gunakan 0 dan 1. Lihat stackoverflow.com/a/43840545/117471
Bruno Bronosky
167

Mengapa Anda harus peduli dengan apa yang saya katakan walaupun ada 250+ jawaban yang mendukung

Bukan itu 0 = truedan 1 = false. Yaitu: nol berarti tidak ada kegagalan (sukses) dan bukan nol berarti gagal (tipe N) .

Sementara jawaban yang dipilih secara teknis "benar" tolong jangan cantumkan return 1** pada kode Anda untuk salah . Ini akan memiliki beberapa efek samping yang tidak menguntungkan.

  1. Pengembang yang berpengalaman akan melihat Anda sebagai seorang amatir (karena alasan di bawah).
  2. Pengembang yang berpengalaman tidak melakukan ini (untuk semua alasan di bawah).
  3. Itu rawan kesalahan.
    • Bahkan pengembang yang berpengalaman pun dapat menganggap 0 dan 1 sebagai salah dan benar masing-masing (untuk alasan di atas)
  4. Itu membutuhkan (atau akan mendorong) komentar asing dan konyol.
  5. Ini sebenarnya kurang membantu daripada status pengembalian implisit.

Pelajari beberapa bash

The bash manual mengatakan (penekanan)

kembali [n]

Menyebabkan fungsi shell untuk berhenti mengeksekusi dan mengembalikan nilai n ke pemanggilnya. Jika n tidak diberikan , nilai kembali adalah status keluar dari perintah terakhir yang dijalankan dalam fungsi.

Karena itu, kita tidak perlu menggunakan 0 dan 1 untuk menunjukkan Benar dan Salah. Fakta bahwa mereka melakukannya pada dasarnya pengetahuan sepele yang hanya berguna untuk debugging kode, pertanyaan wawancara, dan meniup pikiran pemula.

Manual bash juga mengatakan

jika tidak, status pengembalian fungsi adalah status keluar dari perintah terakhir yang dijalankan

Manual bash juga mengatakan

( $? ) Memperluas status keluar dari pipa latar depan yang baru saja dieksekusi .

Tunggu sebentar. Pipa? Mari kita beralih ke manual bash sekali lagi.

Pipeline adalah urutan satu atau lebih perintah yang dipisahkan oleh salah satu operator kontrol '|' atau '| &'.

Iya. Mereka mengatakan 1 perintah adalah sebuah pipa. Karena itu, ketiga kutipan tersebut mengatakan hal yang sama.

  • $? memberi tahu Anda apa yang terjadi terakhir.
  • Itu meluap.

Jawabanku

Jadi, sementara @Kambus menunjukkan hal itu dengan fungsi yang begitu sederhana, tidakreturn diperlukan sama sekali. Saya pikir itu sangat sederhana dibandingkan dengan kebutuhan kebanyakan orang yang akan membaca ini.

Mengapa return ?

Jika suatu fungsi akan mengembalikan status keluar perintah terakhirnya, mengapa digunakan return ? Karena itu menyebabkan fungsi untuk berhenti mengeksekusi.

Hentikan eksekusi dalam berbagai kondisi

01  function i_should(){
02      uname="$(uname -a)"
03
04      [[ "$uname" =~ Darwin ]] && return
05
06      if [[ "$uname" =~ Ubuntu ]]; then
07          release="$(lsb_release -a)"
08          [[ "$release" =~ LTS ]]
09          return
10      fi
11
12      false
13  }
14
15  function do_it(){
16      echo "Hello, old friend."
17  }
18
19  if i_should; then
20    do_it
21  fi

Apa yang kita miliki di sini adalah ...

Line 04adalah [-ish] eksplisit yang mengembalikan true karena RHS &&hanya dieksekusi jika LHS benar

Line 09mengembalikan true atau false yang cocok dengan status baris08

Baris 13mengembalikan false karena garis12

(Ya, ini bisa diturunkan, tetapi seluruh contoh dibuat-buat.)

Pola umum lainnya

# Instead of doing this...
some_command
if [[ $? -eq 1 ]]; then
    echo "some_command failed"
fi

# Do this...
some_command
status=$?
if ! $(exit $status); then
    echo "some_command failed"
fi

Perhatikan bagaimana pengaturan statusvariabel demistifikasi makna $?. (Tentu saja Anda tahu apa $?artinya, tetapi seseorang yang kurang berpengetahuan daripada Anda harus Google itu suatu hari nanti. Kecuali jika kode Anda melakukan perdagangan frekuensi tinggi, tunjukkan cinta , tentukan variabelnya.) Tetapi kesimpulan sebenarnya adalah bahwa "jika tidak ada status "atau sebaliknya" jika status keluar "dapat dibacakan dengan lantang dan jelaskan artinya. Namun, yang terakhir mungkin agak terlalu ambisius karena melihat kata itu exitmungkin membuat Anda berpikir itu keluar dari skrip, padahal kenyataannya keluar dari $(...)subkulit.


** Jika Anda benar-benar bersikeras menggunakan return 1untuk false, saya sarankan setidaknya Anda gunakan return 255sebagai gantinya. Ini akan menyebabkan diri Anda di masa depan, atau pengembang lain yang harus menjaga kode Anda mempertanyakan "mengapa itu 255?" Maka mereka setidaknya akan memperhatikan dan memiliki kesempatan yang lebih baik untuk menghindari kesalahan.

Bruno Bronosky
sumber
1
@ZeroPhase 1 & 0 untuk false & true akan menjadi konyol. Jika Anda memiliki tipe data biner, tidak ada alasan untuk itu. Apa yang Anda hadapi dalam bash adalah kode status yang mencerminkan keberhasilan (tunggal) dan kegagalan (jamak). Ini " ifsukses lakukan ini, elselakukan itu." Sukses apa? Bisa jadi memeriksa benar / salah, bisa memeriksa string, integer, file, direktori, izin menulis, glob, regex, grep, atau perintah lain yang mengalami kegagalan .
Bruno Bronosky
3
Menghindari penggunaan standar 0/1 sebagai nilai balik hanya karena itu tumpul dan cenderung membingungkan adalah konyol. Seluruh bahasa shell tumpul dan cenderung kebingungan. Bash sendiri menggunakan 0/1 = true / konvensi palsu sendiri truedan falseperintah. Artinya, kata kunci truesecara harfiah mengevaluasi ke kode status 0. Selain itu, pernyataan if-then secara alami beroperasi pada boolean, bukan kode keberhasilan. Jika kesalahan terjadi selama operasi boolean, itu seharusnya tidak mengembalikan benar atau salah tetapi hanya menghentikan eksekusi. Kalau tidak, Anda mendapatkan false positive (permainan kata-kata).
Beejor
1
"Jika! $ (keluar $ status); lalu" - Itu layak mendapatkan penjelasan yang lebih baik. Itu tidak intuitif. Saya harus memikirkan apakah program akan keluar sebelum mencetak pesan, atau tidak. Jawaban Anda yang lain baik, tetapi itu merusaknya.
Craig Hicks
1
@BrunoBronosky Saya membaca jawaban Anda dan saya pikir saya mengerti perbedaan antara pipa konten (stdin-> stdout) dan kode kesalahan dalam nilai pengembalian. Namun, saya tidak mengerti mengapa penggunaan return 1harus dihindari. Dengan asumsi saya memiliki validatefungsi, saya merasa masuk akal return 1jika validasi gagal. Bagaimanapun, itu adalah alasan untuk menghentikan eksekusi skrip jika tidak ditangani dengan benar (misalnya, ketika menggunakan set -e).
JepZ
1
@Jepz itu pertanyaan yang bagus. Anda benar, itu return 1valid. Perhatian saya adalah semua komentar di sini yang mengatakan “0 = benar 1 = salah adalah [masukkan kata negatif]”. Orang-orang itu mungkin akan membaca kode Anda suatu hari nanti. Jadi bagi mereka melihat return 255(atau 42 atau bahkan 2) harus membantu mereka lebih memikirkannya dan tidak menganggapnya “benar”. set -emasih akan menangkapnya.
Bruno Bronosky
31
myfun(){
    [ -d "$1" ]
}
if myfun "path"; then
    echo yes
fi
# or
myfun "path" && echo yes
Kambus
sumber
1
Bagaimana dengan negasi?
einpoklum
Bagaimana dengan itu, @einpoklum?
Mark Reed
@ MarkReed: Maksud saya, tambahkan case "lain" ke contoh Anda.
einpoklum
myfun "path" || echo no
Hrobky
13

Hati-hati saat memeriksa direktori hanya dengan opsi -d!
jika variabel $ 1 kosong, cek masih akan berhasil. Yang pasti, periksa juga bahwa variabel tidak kosong.

#! /bin/bash

is_directory(){

    if [[ -d $1 ]] && [[ -n $1 ]] ; then
        return 0
    else
        return 1
    fi

}


#Test
if is_directory $1 ; then
    echo "Directory exist"
else
    echo "Directory does not exist!" 
fi
RenRen
sumber
1
Saya tidak yakin bagaimana ini menjawab pertanyaan yang diajukan. Meskipun senang mengetahui bahwa $ 1 yang kosong dapat mengembalikan true ketika kosong, itu tidak memberikan wawasan tentang bagaimana mengembalikan benar atau salah dari fungsi bash. Saya akan menyarankan membuat pertanyaan baru "Apa yang terjadi ketika Anda melakukan tes pada variabel shell kosong?" Dan kemudian memposting ini sebagai jawabannya.
DRaehal
3
Perhatikan, bahwa jika Anda menambahkan kutipan yang tepat ke $1( "$1") maka Anda tidak perlu memeriksa variabel kosong. The [[ -d "$1" ]]akan gagal karena ini ""bukan sebuah direktori.
Morgents
3

Saya menemukan titik (belum disebutkan secara eksplisit?) Yang saya tersandung. Artinya, bukan bagaimana mengembalikan boolean, melainkan bagaimana cara mengevaluasinya dengan benar!

Saya mencoba mengatakannya if [ myfunc ]; then ..., tetapi itu salah. Anda tidak harus menggunakan tanda kurung! if myfunc; then ...adalah cara untuk melakukannya.

Seperti di @Bruno dan yang lainnya menegaskan, truedan falsemerupakan perintah , bukan nilai! Itu sangat penting untuk memahami boolean dalam skrip shell.

Dalam posting ini, saya menjelaskan dan melakukan demo menggunakan variabel boolean : https://stackoverflow.com/a/55174008/3220983 . Saya sangat menyarankan memeriksa itu, karena sangat erat hubungannya.

Di sini, saya akan memberikan beberapa contoh pengembalian dan evaluasi boolean dari fungsi:

Ini:

test(){ false; }                                               
if test; then echo "it is"; fi                                 

Tidak menghasilkan output gema. (mis. false mengembalikan false)

test(){ true; }                                                
if test; then echo "it is"; fi                                 

Menghasilkan:

it is                                                        

(yaitu true mengembalikan true)

Dan

test(){ x=1; }                                                
if test; then echo "it is"; fi                                 

Menghasilkan:

it is                                                                           

Karena 0 (yaitu benar) dikembalikan secara implisit .

Sekarang, inilah yang mengacaukan saya ...

test(){ true; }                                                
if [ test ]; then echo "it is"; fi                             

Menghasilkan:

it is                                                                           

DAN

test(){ false; }                                                
if [ test ]; then echo "it is"; fi                             

JUGA menghasilkan:

it is                                                                           

Menggunakan tanda kurung di sini menghasilkan false positive ! (Saya menyimpulkan hasil perintah "luar" adalah 0.)

Pengambilan utama dari posting saya adalah: jangan gunakan tanda kurung untuk mengevaluasi fungsi boolean (atau variabel) seperti yang Anda lakukan untuk pemeriksaan kesetaraan tipikal misalnya if [ x -eq 1 ]; then...!

Terima kasih
sumber
2

Gunakan perintah trueatau falsesegera sebelum Anda return, maka returntanpa parameter. The returnotomatis akan menggunakan nilai perintah terakhir Anda.

Memberikan argumen returntidak konsisten, spesifik jenis dan cenderung kesalahan jika Anda tidak menggunakan 1 atau 0. Dan seperti komentar sebelumnya, menggunakan 1 atau 0 di sini bukan cara yang tepat untuk mendekati fungsi ini.

#!/bin/bash

function test_for_cat {
    if [ $1 = "cat" ];
    then
        true
        return
    else
        false
        return
    fi
}

for i in cat hat;
do
    echo "${i}:"
    if test_for_cat "${i}";
    then
        echo "- True"
    else
        echo "- False"
    fi
done

Keluaran:

$ bash bash_return.sh

cat:
- True
hat:
- False
mrteatime
sumber
1

Mungkin berhasil jika Anda menulis ulang ini function myfun(){ ... return 0; else return 1; fi;}sebagai ini function myfun(){ ... return; else false; fi;}. Itu adalah jika falseadalah instruksi terakhir dalam fungsi Anda mendapatkan hasil yang salah untuk seluruh fungsi tetapi returnmengganggu fungsi dengan hasil yang sebenarnya. Saya percaya setidaknya benar untuk penerjemah bash saya.

Keponakan
sumber
1

Saya menemukan bentuk terpendek untuk menguji fungsi keluarannya secara sederhana

do_something() {
    [[ -e $1 ]] # e.g. test file exists
}

do_something "myfile.txt" || { echo "File doesn't exist!"; exit 1; }
Roy Shilkrot
sumber
1

Untuk alasan keterbacaan kode, saya yakin mengembalikan benar / salah harus:

  • berada di satu baris
  • jadilah satu perintah
  • mudah diingat
  • sebutkan kata kunci returndiikuti oleh kata kunci lain ( trueatau false)

Solusi saya adalah return $(true)atau return $(false)seperti yang ditunjukkan:

is_directory()
{
    if [ -d "${1}" ]; then
        return $(true)
    else
        return $(false)
    fi
}
Charlie
sumber
0

Menindaklanjuti @Bruno Bronosky dan @mrteatime, saya menawarkan saran bahwa Anda hanya menulis boolean return "mundur". Inilah yang saya maksud:

foo()
{
    if [ "$1" == "bar" ]; then
        true; return
    else
        false; return
    fi;
}

Itu menghilangkan persyaratan dua baris jelek untuk setiap pernyataan kembali.

Terima kasih
sumber