Lihat BashFAQ # 105 perihal: mengapa set -edianggap jauh lebih rentan terhadap kesalahan daripada penanganan kesalahan yang ditulis tangan. (Jika terburu-buru, lewati analogi di atas untuk latihan di bawah).
Charles Duffy
Jawaban:
70
Ini pada dasarnya mengambil argumen baris perintah apa pun yang diteruskan ke entrypoint.shdan menjalankannya sebagai perintah. Intinya pada dasarnya adalah "Lakukan segala sesuatu dalam skrip .sh ini, lalu di shell yang sama jalankan perintah yang dilewatkan pengguna pada baris perintah".
Perhatikan juga bahwa exec "$@"akan menggantikan proses yang sedang berjalan dengan proses baru yang muncul untuk argumen yang diteruskan. Penting untuk pensinyalan Docker: stackoverflow.com/a/32261019/99717
Hawkeye Parker
34
set -emenetapkan opsi shell untuk segera keluar jika ada perintah yang dijalankan keluar dengan kode keluar bukan nol. Skrip akan kembali dengan kode keluar dari perintah yang gagal. Dari halaman manual bash:
set -e:
Keluar segera jika pipeline (yang mungkin terdiri dari satu perintah sederhana), daftar, atau perintah gabungan (lihat SHELL GRAMMAR di atas), keluar dengan status bukan nol. Shell tidak keluar jika perintah yang gagal adalah bagian dari daftar perintah segera setelah beberapa saat atau hingga kata kunci, bagian dari pengujian yang mengikuti kata-kata yang dicadangkan if atau elif, bagian dari perintah apa pun yang dieksekusi dalam a && atau || daftar kecuali perintah setelah && atau || terakhir, perintah apa pun dalam pipeline kecuali yang terakhir, atau jika nilai kembalian perintah dibalik dengan!. Jika perintah gabungan selain subkulit mengembalikan status bukan nol karena perintah gagal saat -e diabaikan, shell tidak akan keluar. Perangkap pada ERR, jika disetel, dijalankan sebelum shell keluar.
Jika perintah gabungan atau fungsi shell dijalankan dalam konteks di mana -e diabaikan, tidak ada perintah yang dieksekusi dalam perintah gabungan atau fungsi tubuh yang akan terpengaruh oleh pengaturan -e, bahkan jika -e disetel dan perintah mengembalikan a status kegagalan. Jika perintah gabungan atau fungsi shell menyetel -e saat mengeksekusi dalam konteks di mana -e diabaikan, pengaturan tersebut tidak akan berpengaruh hingga perintah gabungan atau perintah yang berisi pemanggilan fungsi selesai.
exec "$@"biasanya digunakan untuk membuat entrypoint melewati yang kemudian menjalankan perintah buruh pelabuhan. Ini akan menggantikan shell yang sedang berjalan dengan perintah yang "$@"mengarah ke. Secara default, variabel itu menunjuk ke argumen baris perintah.
Jika Anda memiliki gambar dengan titik masuk yang mengarah ke entrypoint.sh, dan Anda menjalankan penampung Anda sebagai docker run my_image server start, itu akan diterjemahkan menjadi berjalan entrypoint.sh server startdi penampung. Pada baris exec entrypoint.sh, shell yang dijalankan sebagai pid 1 akan menggantikan dirinya dengan perintah server start.
Ini penting untuk penanganan sinyal. Tanpa menggunakan exec, contoh server startdi atas akan berjalan sebagai pid lain, dan setelah keluar, Anda akan kembali ke skrip shell Anda. Dengan shell di pid 1, SIGTERM akan diabaikan secara default. Itu berarti sinyal berhenti anggun yang docker stopdikirim ke penampung Anda, tidak akan pernah diterima oleh serverproses tersebut. Setelah 10 detik (secara default), docker stopakan menyerah pada shutdown yang anggun dan mengirim SIGKILL yang akan memaksa aplikasi Anda untuk keluar, tetapi dengan potensi kehilangan data atau koneksi jaringan tertutup, pengembang aplikasi tersebut dapat membuat kode jika mereka menerima sinyal. Ini juga berarti wadah Anda akan selalu membutuhkan waktu 10 detik untuk berhenti.
Perhatikan bahwa dengan perintah shell seperti shiftdan set --, Anda dapat mengubah nilai "$@". Misalnya, inilah bagian singkat dari skrip yang menghapus /bin/sh -c "..."dari perintah yang dapat muncul jika Anda menggunakan sintaks shell buruh pelabuhan untuk CMD:
Lihat spesifikasi POSIXtest , yang menandai -ausang. [ "$#" -gt 1 ] && [ "$1" = /bin/sh ]adalah pengganti yang benar (tidak perlu x"$1"peretasan jika hanya menggunakan sintaks yang tidak usang).
Charles Duffy
Juga, shift 2; set -- $1sama sekali tidak sama dengan bagaimana evalmengurai string. Pertimbangkan /bin/sh -c 'printf "%s\n" "hello world" "goodbye world"', jika Anda menginginkan kasus pengujian konkret, dan lihat Bash tidak mengurai tanda kutip saat mengonversi string menjadi argumen .
Charles Duffy
@CharlesDuffy terima kasih atas tip tentang opsi usang, saya yakin saya akan membuat kesalahan ini lagi, kebiasaan lama sulit hilang. Dengan eval, saya yakin saya masih ingin itu mencerminkan perilaku yang /bin/sh -cakan ada pada tali, tapi tolong beri tahu saya jika saya melewatkan sesuatu.
BMitch
30
set -e - keluar dari skrip jika ada perintah yang gagal (nilai bukan nol)
exec "$@"- akan mengarahkan variabel input, lihat lebih lanjut di sini
set -e
dianggap jauh lebih rentan terhadap kesalahan daripada penanganan kesalahan yang ditulis tangan. (Jika terburu-buru, lewati analogi di atas untuk latihan di bawah).Jawaban:
Ini pada dasarnya mengambil argumen baris perintah apa pun yang diteruskan ke
entrypoint.sh
dan menjalankannya sebagai perintah. Intinya pada dasarnya adalah "Lakukan segala sesuatu dalam skrip .sh ini, lalu di shell yang sama jalankan perintah yang dilewatkan pengguna pada baris perintah".Lihat:
sumber
exec "$@"
akan menggantikan proses yang sedang berjalan dengan proses baru yang muncul untuk argumen yang diteruskan. Penting untuk pensinyalan Docker: stackoverflow.com/a/32261019/99717set -e
menetapkan opsi shell untuk segera keluar jika ada perintah yang dijalankan keluar dengan kode keluar bukan nol. Skrip akan kembali dengan kode keluar dari perintah yang gagal. Dari halaman manual bash:exec "$@"
biasanya digunakan untuk membuat entrypoint melewati yang kemudian menjalankan perintah buruh pelabuhan. Ini akan menggantikan shell yang sedang berjalan dengan perintah yang"$@"
mengarah ke. Secara default, variabel itu menunjuk ke argumen baris perintah.Jika Anda memiliki gambar dengan titik masuk yang mengarah ke entrypoint.sh, dan Anda menjalankan penampung Anda sebagai
docker run my_image server start
, itu akan diterjemahkan menjadi berjalanentrypoint.sh server start
di penampung. Pada baris execentrypoint.sh
, shell yang dijalankan sebagai pid 1 akan menggantikan dirinya dengan perintahserver start
.Ini penting untuk penanganan sinyal. Tanpa menggunakan
exec
, contohserver start
di atas akan berjalan sebagai pid lain, dan setelah keluar, Anda akan kembali ke skrip shell Anda. Dengan shell di pid 1, SIGTERM akan diabaikan secara default. Itu berarti sinyal berhenti anggun yangdocker stop
dikirim ke penampung Anda, tidak akan pernah diterima olehserver
proses tersebut. Setelah 10 detik (secara default),docker stop
akan menyerah pada shutdown yang anggun dan mengirim SIGKILL yang akan memaksa aplikasi Anda untuk keluar, tetapi dengan potensi kehilangan data atau koneksi jaringan tertutup, pengembang aplikasi tersebut dapat membuat kode jika mereka menerima sinyal. Ini juga berarti wadah Anda akan selalu membutuhkan waktu 10 detik untuk berhenti.Perhatikan bahwa dengan perintah shell seperti
shift
danset --
, Anda dapat mengubah nilai"$@"
. Misalnya, inilah bagian singkat dari skrip yang menghapus/bin/sh -c "..."
dari perintah yang dapat muncul jika Anda menggunakan sintaks shell buruh pelabuhan untukCMD
:# convert `/bin/sh -c "server start"` to `server start` if [ $# -gt 1 ] && [ x"$1" = x"/bin/sh" ] && [ x"$2" = x"-c" ]; then shift 2 eval "set -- $1" fi .... exec "$@"
sumber
test
, yang menandai-a
usang.[ "$#" -gt 1 ] && [ "$1" = /bin/sh ]
adalah pengganti yang benar (tidak perlux"$1"
peretasan jika hanya menggunakan sintaks yang tidak usang).shift 2; set -- $1
sama sekali tidak sama dengan bagaimanaeval
mengurai string. Pertimbangkan/bin/sh -c 'printf "%s\n" "hello world" "goodbye world"'
, jika Anda menginginkan kasus pengujian konkret, dan lihat Bash tidak mengurai tanda kutip saat mengonversi string menjadi argumen .eval
, saya yakin saya masih ingin itu mencerminkan perilaku yang/bin/sh -c
akan ada pada tali, tapi tolong beri tahu saya jika saya melewatkan sesuatu.set -e
- keluar dari skrip jika ada perintah yang gagal (nilai bukan nol)exec "$@"
- akan mengarahkan variabel input, lihat lebih lanjut di sinisumber
exec
memang memiliki mode penggunaan di mana ia melakukan pengalihan, tetapi ini bukan mode itu.