Apa yang dilakukan 'set -e', dan mengapa itu dianggap berbahaya?

147

Pertanyaan ini muncul di kuis pra-wawancara dan itu membuat saya gila. Adakah yang bisa menjawab ini dan menenangkanku? Kuis tidak memiliki referensi ke shell tertentu tetapi deskripsi pekerjaan adalah untuk sa unix. sekali lagi pertanyaannya hanyalah ...

Apa yang dilakukan 'set -e', dan mengapa itu dianggap berbahaya?

egorgry
sumber
Berikut utas terkait: stackoverflow.com/questions/64786/error-handling-in-bash/…
Stefan Lasiewski

Jawaban:

154

set -e menyebabkan shell keluar jika subkomand atau pipa mengembalikan status tidak nol.

Jawaban pewawancara yang mungkin dicari adalah:

Akan berbahaya menggunakan "set -e" saat membuat skrip init.d:

Dari http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -

Hati-hati menggunakan skrip set -e in init.d. Menulis skrip init.d yang benar membutuhkan menerima berbagai status keluar kesalahan ketika daemon sudah berjalan atau sudah berhenti tanpa membatalkan skrip init.d, dan fungsi init.d umum pustaka tidak aman untuk memanggil dengan set -e berlaku. Untuk skrip init.d, seringkali lebih mudah untuk tidak menggunakan set -e dan sebagai gantinya memeriksa hasil dari setiap perintah secara terpisah.

Ini adalah pertanyaan yang valid dari sudut pandang pewawancara karena ia mengukur seorang kandidat yang memiliki pengetahuan tentang scripting dan otomatisasi tingkat server

Kaya
sumber
Poin yang bagus. itu akan menghentikan proses booting atas sesuatu yang mungkin merupakan kesalahan teknis, tetapi seharusnya tidak menyebabkan seluruh skrip berhenti
Matt
Meskipun saya setuju dengan stackoverflow.com/questions/78497/… , saya pikir gaya ini cocok dengan bash untuk cek init pendek dan pencatatan atau penambalan monyet lingkungan lainnya sebelum mengeksekusi beban kerja target. Kami menggunakan kebijakan yang sama untuk gambar buruh pelabuhan kami skrip init.
joshperry
33

Dari bash(1):

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ││  list
                  except  the  command  following  the final && or ││, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.

Sayangnya saya tidak cukup kreatif untuk berpikir mengapa itu akan berbahaya, selain dari "sisa skrip tidak akan dieksekusi" atau "itu mungkin mungkin menutupi masalah nyata".

Ignacio Vazquez-Abrams
sumber
1
topeng masalah nyata / lainnya, ya, kurasa aku bisa melihat bahwa ... aku sedang berjuang untuk datang dengan contoh itu menjadi berbahaya, juga ...
cpbills
Ada manualnya untuk set! Saya selalu berakhir di builtin(1). Terima kasih.
Jared Beck
bagaimana saya tahu bahwa dokumentasi setitu harus ditemukan man 1 bash?? dan bagaimana orang dapat menemukan -eopsi untuk setkata kunci di halaman manual yang sangat besar? Anda tidak bisa hanya /-emencari di sini
phil294
@Blauhirn menggunakanhelp set
phil294
19

Perlu dicatat bahwa set -edapat dinyalakan dan dimatikan untuk berbagai bagian naskah. Tidak harus aktif untuk eksekusi seluruh skrip. Itu bahkan bisa diaktifkan secara kondisional. Yang mengatakan, saya tidak pernah menggunakannya karena saya melakukan penanganan kesalahan saya sendiri (atau tidak).

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status

Juga patut dicatat adalah ini dari bagian halaman Bash pada trapperintah yang juga menjelaskan bagaimana set -efungsi dalam keadaan tertentu.

Perangkap ERR tidak dieksekusi jika perintah yang gagal adalah bagian dari daftar perintah segera setelah beberapa saat atau sampai kata kunci, bagian dari pengujian dalam pernyataan if, bagian dari perintah yang dieksekusi dalam daftar&& atau ⎪⎪, atau jika perintah itu nilai balik sedang dibalik via! Ini adalah kondisi yang sama yang dipatuhi oleh opsi errexit.

Jadi ada beberapa kondisi di mana status bukan nol tidak akan menyebabkan keluar.

Saya pikir bahayanya adalah tidak memahami kapan harus set -ebermain dan kapan tidak dan mengandalkannya dengan salah di bawah beberapa asumsi yang tidak valid.

Silakan juga lihat BashFAQ / 105 Mengapa set -e (atau set -o errexit, atau trap ERR) tidak melakukan apa yang saya harapkan?

Dennis Williamson
sumber
Meskipun sedikit menyimpang dari pertanyaan, jawaban ini persis seperti yang saya cari: mematikannya hanya untuk sebagian kecil dari naskah!
Stephan Bijzitter
13

Ingatlah ini adalah kuis untuk wawancara kerja. Pertanyaan-pertanyaan mungkin telah ditulis oleh staf saat ini, dan mereka mungkin salah. Ini tidak selalu buruk, dan semua orang membuat kesalahan, dan pertanyaan wawancara sering duduk di sudut yang gelap tanpa ulasan, dan hanya keluar selama wawancara.

Sangat mungkin bahwa 'set -e' tidak melakukan apa pun yang kami anggap "berbahaya". Tetapi penulis pertanyaan itu mungkin secara keliru percaya bahwa 'set-e' berbahaya, karena ketidaktahuan atau bias mereka sendiri. Mungkin mereka menulis skrip buggy shell, bomnya mengerikan, dan mereka keliru mengira bahwa 'set -e' yang harus disalahkan, padahal sebenarnya mereka lalai menulis pengecekan kesalahan yang tepat.

Saya telah berpartisipasi dalam mungkin 40 wawancara kerja selama 2 tahun terakhir, dan pewawancara kadang-kadang mengajukan pertanyaan yang salah, atau memiliki jawaban yang salah.

Atau mungkin itu pertanyaan jebakan, yang akan timpang, tetapi tidak sepenuhnya tak terduga.

Atau mungkin ini penjelasan yang bagus: http://www.mail-archive.com/[email protected]/msg473314.html

Stefan Lasiewski
sumber
1
+1 Saya berada di kapal yang sama, dan banyak wawancara 'teknis' yang saya tangani setidaknya sedikit salah kaprah.
cpbills
1
Wow. Temuan bagus di daftar debian. +1 untuk ketidaktahuan pertanyaan wawancara. Saya ingat pernah berdebat jawaban netback sekali karena saya 100% yakin saya benar. Mereka berkata tidak. Saya pulang ke rumah dan mencari di Google, saya benar.
egorgry
9

set -e memberitahu bash, dalam sebuah skrip, untuk keluar kapan pun sesuatu mengembalikan nilai pengembalian yang tidak nol.

Saya bisa melihat bagaimana itu akan mengganggu, dan buggy, tidak yakin tentang berbahaya, kecuali jika Anda telah membuka izin pada sesuatu, dan sebelum Anda dapat membatasi mereka lagi, skrip Anda mati.

cpbills
sumber
Itu menarik. Saya tidak memikirkan izin yang dibuka dalam skrip yang mati sebelum waktunya tetapi saya bisa melihat itu dianggap berbahaya. Bagian yang menyenangkan dari kuis ini adalah bahwa Anda tidak dapat menggunakan bahan referensi seperti man atau google dan jika Anda tidak dapat menjawab sepenuhnya jangan menjawabnya sama sekali.
Egorgry
6
itu konyol, saya akan mempertimbangkan kembali majikan ini ... bercanda (semacam). itu baik untuk memiliki basis pengetahuan yang kuat, tetapi setengah dari pekerjaan TI adalah mengetahui / di mana / untuk menemukan informasi ... mudah-mudahan mereka cukup pintar untuk mempertimbangkan hal itu saat menilai pelamar. di samping catatan, saya melihat / tidak / menggunakan untuk set -e, pada kenyataannya, bagi saya itu berbicara tentang malas untuk mengabaikan memeriksa kesalahan dalam skrip Anda ... jadi ingatlah, dengan majikan ini, juga ... jika mereka menggunakannya banyak, seperti apa skrip mereka, yang pasti harus Anda pertahankan ...
cpbills
titik bagus @cpbills. Saya juga tidak melihat kegunaan untuk set -e dalam sebuah skrip dan mungkin itu sebabnya saya sangat bingung. Saya ingin memposting semua pertanyaan karena ada yang benar-benar bagus dan ada yang benar-benar aneh dan acak seperti ini.
egorgry
Saya telah melihatnya di banyak sistem cron job ...
Andrew
8

set -emenghentikan skrip jika kode keluar yang bukan nol ditemukan, kecuali dalam kondisi tertentu. Untuk meringkas bahaya penggunaannya dalam beberapa kata: itu tidak berperilaku seperti yang orang pikirkan.

Menurut pendapat saya, itu harus dianggap sebagai peretasan berbahaya yang terus ada untuk tujuan kompatibilitas saja. The set -epernyataan tidak berubah shell dari bahasa yang menggunakan kode kesalahan ke dalam bahasa yang menggunakan aliran kontrol pengecualian-seperti, itu hanya buruk mencoba untuk meniru perilaku itu.

Greg Wooledge banyak bicara tentang bahaya set -e:

Pada tautan kedua, ada berbagai contoh perilaku tidak intuitif dan tidak terduga set -e.

Beberapa contoh perilaku tidak intuitif set -e(beberapa diambil dari tautan wiki di atas):

  • set -e
    x = 0
    biarkan x ++
    gema "x adalah $ x"
    Hal di atas akan menyebabkan skrip shell keluar secara prematur, karena let x++mengembalikan 0, yang diperlakukan oleh letkata kunci sebagai nilai palsu dan berubah menjadi kode keluar yang tidak nol. set -epemberitahuan ini, dan diam-diam mengakhiri skrip.
  • set -e
    [-d / opt / foo] && echo "Peringatan: foo sudah diinstal. Akan ditimpa." > & 2
    gema "Memasang foo ..."
    

    Di atas berfungsi seperti yang diharapkan, mencetak peringatan jika /opt/foosudah ada.

    set -e
    check_previous_install () {
        [-d / opt / foo] && echo "Peringatan: foo sudah diinstal. Akan ditimpa." > & 2
    }
    
    periksa_previous_install
    gema "Memasang foo ..."
    

    Di atas, meskipun satu-satunya perbedaan adalah bahwa satu baris telah di refactored menjadi suatu fungsi, akan berakhir jika /opt/footidak ada. Ini karena fakta bahwa itu bekerja pada awalnya adalah pengecualian khusus untuk set -eperilaku. Saat a && bmengembalikan nol, diabaikan oleh set -e. Namun, sekarang karena fungsinya, kode keluar dari fungsi sama dengan kode keluar dari perintah itu, dan fungsi yang mengembalikan bukan nol akan secara diam-diam menghentikan skrip.

  • set -e
    IFS = $ '\ n' baca -d '' -r -a config_vars <config
    

    Di atas akan membaca array config_varsdari file config. Seperti yang mungkin dimaksudkan oleh penulis, itu berakhir dengan kesalahan jika confighilang. Karena penulis mungkin tidak bermaksud, itu diam-diam berakhir jika configtidak berakhir pada baris baru. Jika set -etidak digunakan di sini, maka config_varsakan berisi semua baris file apakah itu berakhir pada baris baru atau tidak.

    Pengguna Teks Sublime (dan editor teks lainnya yang menangani baris baru secara salah), berhati-hatilah.

  • set -e
    
    should_audit_user () {
        grup grup lokal = "$ (grup" $ 1 ")"
        untuk grup dalam $ grup; melakukan
            if ["$ group" = audit]; lalu kembalikan 0; fi
        Selesai
        kembali 1
    }
    
    if should_audit_user "$ user"; kemudian
        logger 'Blah'
    fi
    

    Penulis di sini mungkin secara wajar berharap bahwa jika karena alasan tertentu pengguna $usertidak ada, maka groupsperintah akan gagal dan skrip akan berhenti alih-alih membiarkan pengguna melakukan beberapa tugas tanpa diaudit. Namun, dalam hal ini set -epemutusan hubungan kerja tidak pernah berlaku. Jika $usertidak dapat ditemukan karena alasan tertentu, alih-alih menghentikan skrip, should_audit_userfungsi hanya akan mengembalikan data yang salah seolah-olah set -etidak berlaku.

    Ini berlaku untuk fungsi apa pun yang dipanggil dari bagian kondisi ifpernyataan, tidak peduli seberapa dalam bersarang, di mana pun didefinisikan, tidak peduli bahkan jika Anda menjalankannya set -elagi. Penggunaan ifpada titik mana pun sepenuhnya menonaktifkan efek set -esampai blok kondisi sepenuhnya dieksekusi. Jika penulis tidak mengetahui jebakan ini, atau tidak tahu seluruh tumpukan panggilan mereka dalam semua situasi yang memungkinkan di mana suatu fungsi dapat dipanggil, maka mereka akan menulis kode kereta dan rasa aman palsu yang diberikan oleh set -eakan setidaknya sebagian untuk menyalahkan.

    Bahkan jika penulis sepenuhnya menyadari jebakan ini, solusinya adalah menulis kode dengan cara yang sama seperti orang akan menulisnya tanpa set -e, secara efektif membuat pergantian yang kurang dari berguna; tidak hanya penulis harus menulis kode penanganan kesalahan manual seolah-olah set -etidak berlaku, tetapi kehadiran set -emungkin menipu mereka untuk berpikir bahwa mereka tidak perlu melakukannya.

Beberapa kelemahan lebih lanjut dari set -e:

  • Ini mendorong kode ceroboh. Penangan kesalahan benar-benar dilupakan, dengan harapan bahwa apa pun yang gagal akan melaporkan kesalahan dengan cara yang masuk akal. Namun, dengan contoh-contoh seperti di let x++atas, ini tidak terjadi. Jika skrip mati secara tak terduga, biasanya diam-diam, yang menghambat debugging. Jika skrip tidak mati dan Anda mengharapkannya (lihat poin sebelumnya), maka Anda memiliki bug yang lebih halus dan berbahaya di tangan Anda.
  • Ini menuntun orang ke rasa aman yang salah. Lihat lagi iftitik peluru -kondisi.
  • Tempat di mana shell berakhir tidak konsisten antara versi shell atau shell. Dimungkinkan untuk secara tidak sengaja menulis skrip yang berperilaku berbeda pada versi bash yang lebih lama karena perilaku set -etweak antara versi tersebut.

set -eadalah masalah yang diperdebatkan, dan beberapa orang menyadari masalah di sekitarnya yang merekomendasikannya, sementara yang lain hanya merekomendasikan untuk berhati-hati saat aktif untuk mengetahui perangkapnya. Ada banyak pemula skrip shell yang merekomendasikan set -esemua skrip sebagai catch-all untuk kondisi kesalahan, tetapi dalam kehidupan nyata itu tidak bekerja seperti itu.

set -e bukan pengganti untuk pendidikan.

Score_Under
sumber
2

Saya akan mengatakan itu berbahaya karena Anda tidak mengontrol lagi alur skrip Anda. Skrip dapat mengakhiri selama salah satu perintah yang dipanggil skrip mengembalikan nol. Jadi yang harus Anda lakukan adalah melakukan sesuatu yang mengubah perilaku atau output dari salah satu komponen, dan Anda bisa mengakhiri skrip utama. Ini mungkin lebih dari masalah gaya, tetapi pasti memiliki konsekuensi. Bagaimana jika skrip utama Anda seharusnya menetapkan beberapa bendera, dan bukan karena itu diakhiri lebih awal? Anda akhirnya akan menyalahkan seluruh sistem jika mengasumsikan bahwa bendera tersebut seharusnya ada di sana, atau bekerja dengan nilai default atau nilai lama yang tidak terduga.

Marcin
sumber
Sepenuhnya setuju dengan jawaban ini. Ini tidak berbahaya secara inheren , tetapi ini merupakan peluang untuk keluar lebih awal secara tak terduga.
pboin
1
Apa yang mengingatkan saya pada ini adalah prof C ++ milik saya yang selalu menarik poin dari tugas saya karena tidak memiliki titik masuk tunggal / titik keluar tunggal dalam suatu program. Saya pikir itu murni hal 'prinsip', tetapi bisnis set-e ini pasti menunjukkan bagaimana dan mengapa hal-hal dapat sepenuhnya lepas kendali. Pada akhirnya program adalah tentang kontrol dan determinisme, dan dengan penghentian prematur Anda menyerah keduanya.
Marcin
0

Sebagai contoh jawaban @ Marcin yang lebih kongkrit yang secara pribadi telah menggigit saya, bayangkan ada suatu rm foo/bar.txtbaris di skrip Anda. Biasanya bukan masalah besar jika foo/bar.txtsebenarnya tidak ada. Namun dengan set -ehadir sekarang skrip Anda akan berakhir lebih awal di sana! Ups.

Suan
sumber