Script Bash memproses sejumlah perintah secara paralel

196

Saya memiliki skrip bash yang terlihat seperti ini:

#!/bin/bash
wget LINK1 >/dev/null 2>&1
wget LINK2 >/dev/null 2>&1
wget LINK3 >/dev/null 2>&1
wget LINK4 >/dev/null 2>&1
# ..
# ..
wget LINK4000 >/dev/null 2>&1

Tetapi memproses setiap baris sampai perintah selesai kemudian pindah ke yang berikutnya sangat memakan waktu, saya ingin memproses misalnya 20 baris sekaligus kemudian ketika mereka selesai 20 baris lagi diproses.

Saya berpikir wget LINK1 >/dev/null 2>&1 &untuk mengirim perintah ke latar belakang dan melanjutkan, tetapi ada 4000 baris di sini ini berarti saya akan memiliki masalah kinerja, belum lagi menjadi terbatas dalam berapa banyak proses yang harus saya mulai pada saat yang sama sehingga ini bukan yang baik ide.

Salah satu solusi yang saya pikirkan saat ini adalah memeriksa apakah salah satu perintah masih berjalan atau tidak, misalnya setelah 20 baris saya dapat menambahkan loop ini:

while [  $(ps -ef | grep KEYWORD | grep -v grep | wc -l) -gt 0 ]; do
sleep 1
done

Tentu saja dalam hal ini saya harus menambahkan & ke akhir baris! Tapi saya merasa ini bukan cara yang tepat untuk melakukannya.

Jadi bagaimana saya benar-benar mengelompokkan masing-masing 20 baris bersama dan menunggu mereka untuk menyelesaikan sebelum pergi ke 20 baris berikutnya, skrip ini dihasilkan secara dinamis sehingga saya dapat melakukan matematika apa pun yang saya inginkan saat itu sedang dihasilkan, tetapi TIDAK TIDAK harus gunakan wget, itu hanya contoh jadi solusi apa pun yang spesifik wget tidak akan ada gunanya bagiku.

AL-Kateb
sumber
1
waitadalah jawaban yang tepat di sini, tetapi jawaban Anda while [ $(ps …akan jauh lebih baik while pkill -0 $KEYWORD…- menggunakan proctools ... yaitu, untuk alasan yang sah untuk memeriksa apakah suatu proses dengan nama tertentu masih berjalan.
kojiro
Saya pikir pertanyaan ini harus dibuka kembali. QA "kemungkinan duplikat" adalah tentang menjalankan sejumlah program terbatas secara paralel. Suka 2-3 perintah. Pertanyaan ini, bagaimanapun, difokuskan pada menjalankan perintah dalam mis. Loop. (lihat "tetapi ada 4000 baris").
VasiliNovikov
@VasyaNovikov Sudahkah Anda membaca semua jawaban untuk pertanyaan ini dan juga duplikatnya? Setiap jawaban tunggal untuk pertanyaan ini di sini, juga dapat ditemukan dalam jawaban atas pertanyaan duplikat. Itulah tepatnya definisi pertanyaan duplikat. Tidak ada bedanya apakah Anda menjalankan perintah dalam satu lingkaran atau tidak.
robinCTS
@robinCTS ada persimpangan, tetapi pertanyaannya sendiri berbeda. Juga, 6 jawaban paling populer pada kesepakatan QA yang terhubung dengan 2 proses saja.
VasiliNovikov
2
Saya merekomendasikan membuka kembali pertanyaan ini karena jawabannya lebih jelas, lebih bersih, lebih baik, dan jauh lebih tinggi daripada jawaban pada pertanyaan terkait, meskipun tiga tahun lebih baru.
Dan Nissenbaum

Jawaban:

331

Gunakan waitbawaan:

process1 &
process2 &
process3 &
process4 &
wait
process5 &
process6 &
process7 &
process8 &
wait

Untuk contoh di atas, 4 proses process1... process4akan dimulai di latar belakang, dan shell akan menunggu sampai selesai sebelum memulai set berikutnya.

Dari manual GNU :

wait [jobspec or pid ...]

Tunggu hingga proses anak yang ditentukan oleh setiap proses ID pid atau spesifikasi pekerjaan jobspec keluar dan kembalikan status keluar dari perintah terakhir yang menunggu. Jika spesifikasi pekerjaan diberikan, semua proses dalam pekerjaan menunggu. Jika tidak ada argumen yang diberikan, semua proses anak yang sedang aktif menunggu, dan status kembalian adalah nol. Jika jobspec atau pid tidak menentukan proses anak dari shell, status pengembaliannya adalah 127.

devnull
sumber
14
Jadi pada dasarnyai=0; waitevery=4; for link in "${links[@]}"; do wget "$link" & (( i++%waitevery==0 )) && wait; done >/dev/null 2>&1
kojiro
18
Kecuali Anda yakin bahwa setiap proses akan selesai pada waktu yang sama, ini adalah ide yang buruk. Anda perlu memulai pekerjaan baru untuk menjaga total pekerjaan saat ini pada batas tertentu .... paralel adalah jawabannya.
rsaw
1
Apakah ada cara untuk melakukan ini dalam satu lingkaran?
DomainsFeatured
Saya sudah mencoba ini tetapi tampaknya tugas variabel yang dilakukan dalam satu blok tidak tersedia di blok berikutnya. Apakah ini karena mereka adalah proses yang terpisah? Apakah ada cara untuk mengkomunikasikan variabel kembali ke proses utama?
Bobby
97

Lihat paralel . Sintaksnya mirip dengan xargs, tetapi menjalankan perintah secara paralel.

choroba
sumber
13
Ini lebih baik daripada menggunakan wait, karena menangani memulai pekerjaan baru sebagai yang lama selesai, daripada menunggu seluruh batch selesai sebelum memulai yang berikutnya.
chepner
5
Misalnya, jika Anda memiliki daftar tautan dalam suatu file, Anda dapat melakukan cat list_of_links.txt | parallel -j 4 wget {}yang akan membuat empat wgets berjalan pada satu waktu.
Tn. Llama
5
Ada anak baru di kota bernama pexec yang merupakan pengganti parallel.
slashsbin
2
Memberikan contoh akan lebih membantu
jterm
1
parallel --jobs 4 < list_of_commands.sh, di mana list_of_commands.sh adalah file dengan satu perintah (mis. wget LINK1, catatan tanpa &) pada setiap baris. Mungkin perlu dilakukan CTRL+Zdan bgsetelah dibiarkan berjalan di latar belakang.
weiji14
71

Bahkan, xargs bisa menjalankan perintah secara paralel untuk Anda. Ada -P max_procsopsi baris perintah khusus untuk itu. Lihat man xargs.

Vader B
sumber
2
+100 ini bagus karena dibangun dan sangat mudah digunakan dan dapat dilakukan dalam satu-liner
Clay
Cocok digunakan untuk wadah kecil, karena tidak diperlukan paket / ketergantungan tambahan!
Marco Roy
1
Lihat pertanyaan ini untuk contoh: stackoverflow.com/questions/28357997/…
Marco Roy
7

Anda dapat menjalankan 20 proses dan menggunakan perintah:

wait

Script Anda akan menunggu dan melanjutkan ketika semua pekerjaan latar belakang Anda selesai.

Binpix
sumber