Bagaimana cara menunggu dalam skrip bash untuk beberapa subproses yang dihasilkan dari skrip tersebut untuk menyelesaikan dan mengembalikan kode keluar! = 0 ketika salah satu subproses berakhir dengan kode! = 0?
Skrip sederhana:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
Script di atas akan menunggu semua 10 subproses yang muncul, tetapi selalu memberikan status keluar 0 (lihat help wait
). Bagaimana saya bisa memodifikasi skrip ini sehingga akan menemukan status keluar dari subproses yang muncul dan mengembalikan kode keluar 1 ketika salah satu dari subproses berakhir dengan kode! = 0?
Apakah ada solusi yang lebih baik untuk itu daripada mengumpulkan PID dari subproses, menunggu mereka dalam urutan dan jumlah status keluar?
wait -n
, tersedia dalam bash modern untuk kembali hanya ketika perintah pertama / berikutnya selesai.wait -n
memiliki satu masalah kecil: jika tidak ada pekerjaan anak yang tersisa (alias kondisi ras), ia mengembalikan status keluar yang tidak nol (gagal) yang dapat dibedakan dari proses anak yang gagal.Jawaban:
wait
juga (opsional) mengambil PID dari proses untuk menunggu, dan dengan $! Anda mendapatkan PID dari perintah terakhir yang diluncurkan di latar belakang. Ubah loop untuk menyimpan PID dari masing-masing sub-proses menelurkan ke dalam array, dan kemudian loop lagi menunggu pada setiap PID.sumber
for i in $n_procs; do ./procs[${i}] & ; pids[${i}]=$!; done; for pid in ${pids[*]}; do wait $pid; done;
kan?http://jeremy.zawodny.com/blog/archives/010717.html :
sumber
jobs -p
memberikan PID dari subproses yang berada dalam status eksekusi. Ini akan melewati proses jika proses selesai sebelumjobs -p
dipanggil. Jadi, jika salah satu dari subproses berakhir sebelumnyajobs -p
, status keluar proses itu akan hilang.jobs -p
tidak memberikan PID dari subproses, melainkan GPID . Logika menunggu tampaknya berfungsi, itu selalu menunggu di grup jika grup tersebut ada dan pid jika tidak, tapi ada baiknya untuk menyadari .. terutama jika seseorang harus membangun ini dan memasukkan sesuatu seperti mengirim pesan ke subproses di mana huruf sintaks berbeda tergantung pada apakah Anda memiliki PID atau GPID .. yaitukill -- -$GPID
vskill $PID
Berikut adalah contoh sederhana menggunakan
wait
.Jalankan beberapa proses:
Kemudian tunggu mereka dengan
wait
perintah:Atau adil
wait
(tanpa argumen) untuk semua.Ini akan menunggu semua pekerjaan di latar belakang selesai.
Jika
-n
opsi diberikan, menunggu pekerjaan berikutnya berakhir dan mengembalikan status keluarnya.Lihat:
help wait
danhelp jobs
untuk sintaks.Namun downside adalah bahwa ini hanya akan mengembalikan status ID terakhir, jadi Anda perlu memeriksa status untuk setiap subproses dan menyimpannya dalam variabel.
Atau buat fungsi perhitungan Anda untuk membuat beberapa file pada kegagalan (kosong atau dengan log gagal), kemudian periksa file itu jika ada, misalnya
sumber
sleep 20 && true
dansleep 20 && false
- yaitu: ganti dengan fungsi Anda. Untuk memahami&&
dan||
, jalankanman bash
dan ketik '/' (cari) lalu '^ * Daftar' (sebuah regex) kemudian masukkan: man akan gulir ke bawah ke deskripsi&&
dan||
||
menangkap STDERR yang gagal juga.wait
Jika Anda telah menginstal GNU Paralel, Anda dapat melakukan:
GNU Parallel akan memberi Anda kode keluar:
0 - Semua pekerjaan berjalan tanpa kesalahan.
1-253 - Beberapa pekerjaan gagal. Status keluar memberikan jumlah pekerjaan yang gagal
254 - Lebih dari 253 pekerjaan gagal.
255 - Kesalahan lainnya.
Tonton video intro untuk mempelajari lebih lanjut: http://pi.dk/1
sumber
doCalculations
fungsi didefinisikan dalam skrip yang sama (meskipun OP tidak jelas tentang persyaratan ini). Ketika saya mencoba,parallel
katanya/bin/bash: doCalculations: command not found
(dikatakan ini 10 kali untukseq 0 9
contoh di atas). Lihat di sini untuk solusinya.xargs
memiliki beberapa kemampuan untuk meluncurkan pekerjaan secara paralel melalui-P
opsi. Dari sini :export -f doCalculations ; seq 0 9 |xargs -P 0 -n 1 -I{} bash -c "doCalculations {}"
. Keterbatasanxargs
disebutkan dalam halaman manual untukparallel
.doCalculations
bergantung pada variabel lingkungan skrip-internal lainnya (khususPATH
, dll.), Mereka mungkin perluexport
diedit secara eksplisit sebelum diluncurkanparallel
.wget -O - pi.dk/3 | sh
Anda tidak akan mendapatkan kebingungan. Jika paket Anda telah mengacaukan segalanya untuk Anda, saya mendorong Anda untuk mengangkat masalah dengan paket Anda. Variabel dan fungsi harus diekspor (ekspor -f) untuk GNU Paralel untuk melihatnya (lihatman parallel
: gnu.org/software/parallel/… )Bagaimana dengan sederhana:
Memperbarui:
Seperti yang ditunjukkan oleh banyak komentator, hal di atas menunggu semua proses diselesaikan sebelum melanjutkan, tetapi tidak keluar dan gagal jika salah satu dari mereka gagal, itu dapat dilakukan dengan modifikasi berikut yang disarankan oleh @Bryan, @SamBrightman, dan lainnya :
sumber
for pid in $pids; do wait $pid; done
help wait
- dengan beberapa IDwait
mengembalikan kode keluar yang terakhir saja, seperti yang dikatakan @ vlad-frolov di atas.wait
disebut? Ternyata ini bukan masalah: jika Andawait
pada proses yang sudah keluar,wait
akan segera keluar dengan status proses yang sudah keluar. (Terima kasih,bash
penulis!)Inilah yang saya buat sejauh ini. Saya ingin melihat bagaimana cara menginterupsi perintah tidur jika seorang anak berhenti, sehingga seseorang tidak perlu menyetel
WAITALL_DELAY
ke penggunaannya.sumber
Untuk memparalelkan ini ...
Terjemahkan ke ini ...
--max-procs
berdasarkan pada berapa banyak paralelisme yang Anda inginkan (0
berarti "sekaligus").xargs
- tetapi tidak selalu diinstal secara default.for
loop tidak benar-benar diperlukan dalam contoh ini karenaecho $i
pada dasarnya hanya regenerasi output$(whatever_list
). Saya hanya berpikir penggunaanfor
kata kunci membuatnya sedikit lebih mudah untuk melihat apa yang sedang terjadi.Berikut ini contoh kerja yang disederhanakan ...
sumber
--max-procs
: Bagaimana cara mendapatkan jumlah CPU / core di Linux dari baris perintah?Saya tidak percaya itu mungkin dengan fungsionalitas bawaan Bash.
Anda bisa mendapatkan notifikasi ketika seorang anak keluar:
Namun tidak ada cara yang jelas untuk mendapatkan status keluar anak di penangan sinyal.
Mendapatkan status anak itu biasanya merupakan tugas
wait
keluarga fungsi di API POSIX tingkat bawah. Sayangnya dukungan Bash untuk itu terbatas - Anda dapat menunggu satu proses anak tertentu (dan mendapatkan status keluarnya) atau Anda dapat menunggu semuanya , dan selalu mendapatkan hasil 0.Apa yang tampaknya mustahil untuk dilakukan adalah setara
waitpid(-1)
, yang blok sampai setiap proses anak kembali.sumber
Saya melihat banyak contoh bagus yang tercantum di sini, ingin melemparkan milik saya juga.
Saya menggunakan sesuatu yang sangat mirip untuk memulai / menghentikan server / layanan secara paralel dan memeriksa setiap status keluar. Bekerja bagus untukku. Semoga ini bisa membantu seseorang!
sumber
trap "kill 0" EXIT
Ini adalah sesuatu yang saya gunakan:
sumber
Kode berikut akan menunggu penyelesaian semua perhitungan dan mengembalikan status keluar 1 jika salah satu dari doCalculations gagal.
sumber
Cukup simpan hasil dari shell, misalnya dalam file.
sumber
Ini versi saya yang berfungsi untuk beberapa pids, mencatat log jika eksekusi terlalu lama, dan menghentikan subproses jika eksekusi membutuhkan waktu lebih lama dari nilai yang diberikan.
Contoh, tunggu hingga ketiga proses selesai, catat peringatan jika eksekusi membutuhkan lebih dari 5 detik, hentikan semua proses jika eksekusi membutuhkan waktu lebih dari 120 detik. Jangan keluar dari program karena kegagalan.
sumber
Jika Anda memiliki bash 4.2 atau yang lebih baru, yang berikut ini mungkin berguna bagi Anda. Ini menggunakan array asosiatif untuk menyimpan nama tugas dan "kode" mereka serta nama tugas dan pids mereka. Saya juga telah membangun metode pembatasan-tingkat sederhana yang mungkin berguna jika tugas Anda menghabiskan banyak waktu CPU atau I / O dan Anda ingin membatasi jumlah tugas bersamaan.
Script meluncurkan semua tugas di loop pertama dan menggunakan hasilnya di yang kedua.
Ini agak berlebihan untuk kasus-kasus sederhana tetapi memungkinkan untuk hal-hal yang cukup rapi. Sebagai contoh, seseorang dapat menyimpan pesan kesalahan untuk setiap tugas dalam array asosiatif lain dan mencetaknya setelah semuanya beres.
sumber
Saya baru saja memodifikasi skrip untuk latar belakang dan memparalelkan suatu proses.
Saya melakukan beberapa percobaan (pada Solaris dengan bash dan ksh) dan menemukan bahwa 'tunggu' mengeluarkan status keluar jika bukan nol, atau daftar pekerjaan yang mengembalikan keluar tidak nol ketika tidak ada argumen PID yang disediakan. Misalnya
Pesta:
Ksh:
Output ini ditulis ke stderr, jadi solusi sederhana untuk contoh OP dapat berupa:
Sementara ini:
juga akan mengembalikan hitungan tetapi tanpa file tmp. Ini mungkin juga digunakan dengan cara ini, misalnya:
Tapi ini tidak jauh lebih berguna daripada file tmp IMO. Saya tidak dapat menemukan cara yang berguna untuk menghindari file tmp sementara juga menghindari menjalankan "tunggu" dalam sebuah subkulit, yang tidak akan berfungsi sama sekali.
sumber
Saya sudah mencoba ini dan menggabungkan semua bagian terbaik dari contoh lain di sini. Script ini akan menjalankan
checkpids
fungsi ketika ada proses latar belakang keluar, dan mengeluarkan status keluar tanpa menggunakan polling.sumber
set -m
memungkinkan Anda menggunakan fg & bg dalam skripfg
, selain menempatkan proses terakhir di latar depan, memiliki status keluar yang sama dengan proses di latar depanwhile fg
akan berhenti berulang ketika ada yangfg
keluar dengan status keluar yang tidak nolsayangnya ini tidak akan menangani kasus ketika proses di latar belakang keluar dengan status keluar yang tidak nol. (loop tidak akan segera berakhir. Ini akan menunggu proses sebelumnya selesai.)
sumber
Sudah ada banyak jawaban di sini, tapi saya terkejut tidak ada yang menyarankan menggunakan array ... Jadi inilah yang saya lakukan - ini mungkin berguna untuk beberapa di masa depan.
sumber
Ini berfungsi, harus sama baiknya jika tidak lebih baik dari jawaban @ HoverHell!
dan tentu saja, saya telah mengabadikan skrip ini, dalam proyek NPM yang memungkinkan Anda untuk menjalankan perintah bash secara paralel, berguna untuk pengujian:
https://github.com/ORESoftware/generic-subshell
sumber
trap $? $$
tampaknya mengatur kode keluar ke 0 dan PID ke bash shell berjalan saat ini, setiap kali bagi sayaperangkap adalah temanmu. Anda dapat menjebak ERR di banyak sistem. Anda dapat menjebak EXIT, atau pada DEBUG untuk melakukan sepotong kode setelah setiap perintah.
Ini selain semua sinyal standar.
sumber
Itu
set -e
atas membuat skrip Anda berhenti pada kegagalan.expect
akan kembali1
jika ada subjek yang gagal.sumber
Tepat untuk tujuan ini saya menulis sebuah
bash
fungsi yang disebut:for
.Catatan :
:for
tidak hanya mempertahankan dan mengembalikan kode keluar dari fungsi gagal, tetapi juga mengakhiri semua instance berjalan paralel. Yang mungkin tidak diperlukan dalam kasus ini.pemakaian
for.sh
:Referensi
sumber
Saya menggunakan ini baru-baru ini (terima kasih kepada Alnitak):
Dari sana orang dapat dengan mudah memperkirakan, dan memiliki pemicu (menyentuh file, mengirim sinyal) dan mengubah kriteria penghitungan (menghitung file yang disentuh, atau apa pun) untuk menanggapi pemicu itu. Atau jika Anda hanya ingin 'any' non zero rc, matikan saja kunci dari save_status.
sumber
Saya membutuhkan ini, tetapi proses target bukan anak dari shell saat ini, dalam hal
wait $PID
ini tidak berfungsi. Saya memang menemukan alternatif berikut:Itu bergantung pada keberadaan procfs , yang mungkin tidak tersedia (misalnya Mac tidak menyediakannya). Jadi untuk portabilitas, Anda bisa menggunakan ini:
sumber
Menjebak sinyal CHLD mungkin tidak berfungsi karena Anda dapat kehilangan beberapa sinyal jika tiba secara bersamaan.
sumber
solusi untuk menunggu beberapa subproses dan keluar ketika salah satu dari mereka keluar dengan kode status tidak nol adalah dengan menggunakan 'tunggu -n'
kode status '127' adalah untuk proses yang tidak ada yang berarti anak mungkin telah keluar.
sumber
Tunggu semua pekerjaan dan kembalikan kode keluar dari pekerjaan gagal terakhir. Tidak seperti solusi di atas, ini tidak memerlukan penghematan pid. Hanya bg pergi, dan tunggu.
sumber
Mungkin ada kasus di mana proses selesai sebelum menunggu proses. Jika kami memicu menunggu proses yang sudah selesai, itu akan memicu kesalahan seperti pid bukan anak dari shell ini. Untuk menghindari kasus seperti itu, fungsi berikut dapat digunakan untuk menemukan apakah prosesnya selesai atau tidak:
sumber
Saya pikir cara paling lurus ke depan untuk menjalankan pekerjaan secara paralel dan memeriksa status menggunakan file sementara. Sudah ada beberapa jawaban yang sama (misalnya Nietzche-jou dan mug896).
Kode di atas bukan thread aman. Jika Anda khawatir kode di atas akan berjalan bersamaan dengan itu, lebih baik menggunakan nama file yang lebih unik, seperti gagal. $$. Baris terakhir adalah untuk memenuhi persyaratan: "kembalikan kode 1 ketika salah satu dari subproses berakhir dengan kode! = 0?" Saya melemparkan persyaratan tambahan di sana untuk membersihkan. Mungkin lebih jelas untuk menulisnya seperti ini:
Berikut ini adalah cuplikan yang serupa untuk mengumpulkan hasil dari beberapa pekerjaan: Saya membuat direktori sementara, cerita output dari semua tugas sub dalam file terpisah, dan kemudian membuangnya untuk ditinjau. Ini tidak cocok dengan pertanyaan - saya berikan sebagai bonus:
sumber
Saya hampir jatuh ke dalam perangkap menggunakan
jobs -p
untuk mengumpulkan PID, yang tidak berfungsi jika anak sudah keluar, seperti yang ditunjukkan dalam skrip di bawah ini. Solusi yang saya ambil hanyalah meneleponwait -n
N kali, di mana N adalah jumlah anak yang saya miliki, yang kebetulan saya kenal secara deterministik.Keluaran:
sumber