Saya memiliki perintah CMD yang dipanggil dari skrip shell bursa utama saya yang membutuhkan waktu selamanya.
Saya ingin mengubah skrip sebagai berikut:
- Jalankan perintah CMD secara paralel sebagai proses latar belakang (
CMD &
). - Di skrip utama, buat perulangan untuk memantau perintah yang muncul setiap beberapa detik. Loop juga menggemakan beberapa pesan ke stdout yang menunjukkan kemajuan skrip.
- Keluar dari loop ketika perintah yang muncul berakhir.
- Tangkap dan laporkan kode keluar dari proses yang muncul.
Bisakah seseorang memberi saya petunjuk untuk mencapai ini?
Jawaban:
1: Dalam bash,
$!
menyimpan PID dari proses latar belakang terakhir yang dijalankan. Itu akan memberi tahu Anda proses apa yang harus dipantau.4:
wait <n>
menunggu hingga proses dengan PID<n>
selesai (ini akan memblokir sampai proses selesai, jadi Anda mungkin tidak ingin memanggil ini sampai Anda yakin prosesnya selesai), dan kemudian mengembalikan kode keluar dari proses yang sudah selesai.2, 3:
ps
ataups | grep " $! "
dapat memberi tahu Anda apakah prosesnya masih berjalan. Terserah Anda bagaimana memahami hasilnya dan memutuskan seberapa dekat itu dengan penyelesaian. (ps | grep
bukan anti-idiot. Jika Anda punya waktu, Anda dapat menemukan cara yang lebih kuat untuk mengetahui apakah prosesnya masih berjalan).Berikut skrip kerangka:
sumber
ps -p $my_pid -o pid=
tidakgrep
ada yang dibutuhkan.kill -0 $!
adalah cara yang lebih baik untuk mengetahui apakah suatu proses masih berjalan. Itu tidak benar-benar mengirim sinyal apa pun, hanya memeriksa bahwa prosesnya hidup, menggunakan shell bawaan daripada proses eksternal. Seperti yangman 2 kill
dikatakan, "Jika sig adalah 0, maka tidak ada sinyal yang dikirim, tetapi pemeriksaan kesalahan masih dilakukan; ini dapat digunakan untuk memeriksa keberadaan ID proses atau ID grup proses."kill -0
akan mengembalikan bukan nol jika Anda tidak memiliki izin untuk mengirim sinyal ke proses yang sedang berjalan. Sayangnya itu kembali1
dalam kasus ini dan kasus di mana prosesnya tidak ada. Ini membuatnya berguna kecuali jika Anda tidak memiliki proses - yang dapat menjadi kasus bahkan untuk proses yang Anda buat jika alat sepertisudo
terlibat atau jika mereka setuid (dan mungkin menjatuhkan privs).wait
tidak mengembalikan kode keluar dalam variabel$?
. Ini hanya mengembalikan kode keluar dan$?
merupakan kode keluar dari program latar depan terbaru.kill -0
. Berikut adalah referensi peer review dari SO yang menunjukkan komentar CraigRinger sah mengenai:kill -0
akan mengembalikan bukan nol untuk proses yang sedang berjalan ... tetapips -p
akan selalu mengembalikan 0 untuk proses yang sedang berjalan .Beginilah cara saya menyelesaikannya ketika saya memiliki kebutuhan serupa:
sumber
wait
s menyebabkan skrip menunggu hingga akhir dari (setiap) proses.Pid proses anak latar belakang disimpan di $! . Anda dapat menyimpan semua pids proses anak ke dalam sebuah array, misalnya PIDS [] .
Tunggu hingga proses turunan yang ditentukan oleh setiap ID proses pid atau spesifikasi pekerjaan jobspec keluar dan mengembalikan status keluar dari perintah terakhir yang menunggu. Jika spesifikasi pekerjaan diberikan, semua proses dalam pekerjaan akan ditunggu. Jika tidak ada argumen yang diberikan, semua proses anak yang sedang aktif akan ditunggu, dan status kembaliannya adalah nol. Jika opsi -n tersedia, tunggu menunggu pekerjaan apa pun berhenti dan mengembalikan status keluarnya. Jika baik jobspec maupun pid menentukan proses turunan aktif dari shell, status kembaliannya adalah 127.
Gunakan perintah wait Anda bisa menunggu semua proses anak selesai, sementara Anda bisa mendapatkan status keluar dari setiap proses anak melalui $? dan menyimpan status ke STATUS [] . Kemudian Anda dapat melakukan sesuatu tergantung status.
Saya telah mencoba 2 solusi berikut dan semuanya berjalan dengan baik. solution01 lebih ringkas, sedangkan solution02 sedikit rumit.
solusi01
solusi02
sumber
pid=$!; PIDS[$i]=${pid}; ((i+=1))
dapat ditulis lebih sederhana karenaPIDS+=($!)
hanya menambahkan ke array tanpa harus menggunakan variabel terpisah untuk mengindeks atau pid itu sendiri. Hal yang sama berlaku untukSTATUS
array.Seperti yang saya lihat hampir semua jawaban menggunakan utilitas eksternal (kebanyakan
ps
) untuk melakukan polling status proses latar belakang. Ada solusi yang lebih unixesh, menangkap sinyal SIGCHLD. Dalam penangan sinyal harus diperiksa proses anak mana yang dihentikan. Ini dapat dilakukan dengankill -0 <PID>
built-in (universal) atau memeriksa keberadaan/proc/<PID>
direktori (khusus Linux) atau menggunakan filejobs
built-in (pestaspesifik.jobs -l
juga melaporkan pid. Dalam hal ini bidang ke-3 dari keluaran dapat Berhenti | Berjalan | Selesai | Keluar. ).Inilah contoh saya.
Proses yang diluncurkan disebut
loop.sh
. Itu menerima-x
atau angka sebagai argumen. Untuk-x
keluar dengan kode keluar 1. Untuk nomor itu menunggu num * 5 detik. Dalam setiap 5 detik ia mencetak PID-nya.Proses peluncur disebut
launch.sh
:Untuk penjelasan lebih lanjut lihat: Memulai proses dari skrip bash gagal
sumber
for i in ${!pids[@]};
menggunakan ekspansi parameter.sumber
grep -v
. Anda dapat membatasi pencarian di awal baris:grep '^'$pid
Plus, Anda tetap bisa melakukannyaps p $pid -o pid=
. Juga,tail -f
tidak akan berakhir sampai Anda membunuhnya, jadi menurut saya ini bukan cara yang baik untuk mendemonstrasikan ini (setidaknya tanpa menunjukkan itu). Anda mungkin ingin mengarahkan output darips
perintah Anda ke/dev/null
atau itu akan pergi ke layar di setiap iterasi.exit
Penyebab Andawait
harus dilewati - mungkin harus abreak
. Tapi bukankahwhile
/ps
dan ituwait
berlebihan?kill -0 $pid
? Itu tidak benar-benar mengirim sinyal apa pun, hanya memeriksa bahwa prosesnya hidup, menggunakan shell bawaan daripada proses eksternal.bash: kill: (1) - Operation not permitted
Saya akan sedikit mengubah pendekatan Anda. Daripada memeriksa setiap beberapa detik jika perintah masih hidup dan melaporkan pesan, lakukan proses lain yang melaporkan setiap beberapa detik bahwa perintah masih berjalan dan kemudian matikan proses itu saat perintah selesai. Sebagai contoh:
sumber
while kill -0 $pid 2> /dev/null; do X; done
, semoga bermanfaat bagi orang lain di masa depan yang membaca pesan ini;)Tim kami memiliki kebutuhan yang sama dengan skrip yang dijalankan SSH jarak jauh yang kehabisan waktu setelah 25 menit tidak aktif. Berikut adalah solusi dengan loop pemantauan yang memeriksa proses latar belakang setiap detik, tetapi mencetak hanya setiap 10 menit untuk menekan batas waktu tidak aktif.
sumber
Contoh sederhana, mirip dengan solusi di atas. Ini tidak memerlukan pemantauan keluaran proses apa pun. Contoh selanjutnya menggunakan tail untuk mengikuti keluaran.
Gunakan tail untuk mengikuti keluaran proses dan berhenti ketika proses selesai.
sumber
Solusi lain adalah memantau proses melalui sistem file proc (lebih aman daripada ps / grep combo); ketika Anda memulai proses, ia memiliki folder yang sesuai di / proc / $ pid, jadi solusinya bisa
Sekarang Anda dapat menggunakan variabel $ exit_status sesuka Anda.
sumber
Syntax error: "else" unexpected (expecting "done")
Dengan metode ini, skrip Anda tidak harus menunggu proses latar belakang, Anda hanya perlu memantau file sementara untuk mengetahui status keluar.
sekarang, skrip Anda dapat melakukan hal lain sementara Anda hanya perlu terus memantau konten retFile (ini juga dapat berisi informasi lain yang Anda inginkan seperti waktu keluar).
PS: btw, saya mengkodekan berpikir dalam bash
sumber
Ini mungkin melampaui pertanyaan Anda, namun jika Anda khawatir tentang lamanya waktu proses berjalan, Anda mungkin tertarik untuk memeriksa status menjalankan proses latar belakang setelah selang waktu tertentu. Cukup mudah untuk memeriksa PID anak mana yang masih berjalan
pgrep -P $$
, namun saya menemukan solusi berikut untuk memeriksa status keluar dari PID yang sudah kedaluwarsa:keluaran yang mana:
Catatan: Anda dapat mengubah
$pids
variabel string daripada array untuk menyederhanakan hal-hal jika Anda mau.sumber
Solusi saya adalah menggunakan pipa anonim untuk meneruskan status ke loop pemantauan. Tidak ada file sementara yang digunakan untuk bertukar status jadi tidak ada yang perlu dibersihkan. Jika Anda tidak yakin tentang jumlah pekerjaan latar belakang, kondisi istirahat bisa terjadi
[ -z "$(jobs -p)" ]
.sumber