Apakah menggunakan "selagi benar" untuk membuat skrip tetap hidup adalah ide yang bagus?

19

Saya hanya melompat ke dunia unix dari dunia yang berbeda, dan ingin tahu apakah

while true
do
  /someperlscript.pl
done

Script perl itu sendiri secara internal memiliki pengamat folder / file yang dijalankan ketika file diubah di lokasi target.

Apakah ini ( while true) ide yang bagus? Jika tidak, apa pendekatan yang lebih baik dan kuat?

TIA

EDIT: Karena ini tampaknya telah menghasilkan sedikit minat, inilah skenario lengkapnya. Script perl itu sendiri menonton direktori menggunakan pengamat file. Setelah menerima file baru (mereka tiba melalui rsync), ia mengambil yang baru dan memprosesnya. Sekarang file yang masuk mungkin rusak (jangan tanya .. berasal dari raspberry pi), dan terkadang prosesnya mungkin tidak dapat mengatasinya. Saya tidak tahu persis mengapa, karena kami belum mengetahui semua skenario.

TETAPI - jika prosesnya gagal karena beberapa alasan, kami ingin itu berjalan dan berjalan dan berurusan dengan file berikutnya, karena file berikutnya sama sekali tidak terkait dengan yang sebelumnya yang mungkin telah menyebabkan kesalahan.

Biasanya saya akan menggunakan semacam catch all dan membungkus seluruh kode di sekitarnya sehingga TIDAK PERNAH crash. Tetapi tidak yakin untuk perl.

Dari apa yang saya mengerti, menggunakan sesuatu seperti supervisor adalah pendekatan yang bagus untuk ini.

Abhinav Gujjar
sumber
9
Apakah ini Linux atau UNIX? Jika Linux, Anda mungkin ingin memeriksa inotifyAPI sehingga Anda dapat menghindari lingkaran sibuk menunggu file untuk berubah di direktori target Anda.
roaima
Linux-nya, ubuntu
Abhinav Gujjar
Jika Anda tahu file tersebut baik sebelum meninggalkan pi, maka Anda bisa menjalankan checksum seperti md5 atau sha1 dan mengirimkannya bersama dengan file Anda. Kemudian penerima akan tahu jika ada file yang buruk sebelum mencoba memprosesnya. Jika Anda tidak tahu itu, maka Anda masih bisa membangun semacam checksum parsial atau blok atau sesuatu yang serupa ke dalam file data Anda yang menjamin integritas data, sehingga Anda dapat memeriksa hal-hal saat Anda pergi sebelum Anda berkomitmen untuk proses yang bisa gagal. Satu ons pencegahan ...
Joe

Jawaban:

21

Itu tergantung pada seberapa cepat script perl kembali. Jika kembali dengan cepat, Anda mungkin ingin memasukkan jeda kecil di antara eksekusi untuk menghindari beban CPU, misalnya:

while true
do
  /someperlscript.pl
  sleep 1
done

Ini juga akan mencegah babi CPU jika skrip tidak ditemukan atau langsung crash.

Loop mungkin juga lebih baik diimplementasikan dalam skrip perl itu sendiri untuk menghindari masalah ini.

Edit:

Saat Anda menulis, tujuan loop hanya untuk memulai kembali skrip perl jika crash, pendekatan yang lebih baik adalah mengimplementasikannya sebagai layanan yang dipantau tetapi cara yang tepat untuk melakukannya tergantung pada OS. Contoh: Solaris smf, Linux systemd atau restarter berbasis cron.

Jlliagre
sumber
10
Anda bisa melakukannya while sleep 1; do ...untuk menyimpan panggilan yang sebenarnya.
Raphael Ahrens
4
@RaphaelAhrens Memang meskipun itu akan sedikit mengubah perilaku awal. Alternatif until ! sleep 1; do ...; doneakan tetap menyimpan panggilan bawaan tersebut saat memulai skrip dengan segera.
jlliagre
4
Benar-benar setuju sepenuhnya bahwa hot loop harus dihindari dengan panggilan tidur di dalam loop jika Anda akan melakukan ini. Juga setuju bahwa skrip yang digerakkan oleh peristiwa (tidak berlaku, dkk) akan menjadi solusi yang lebih baik. Tetapi menggunakan loop sementara tidak secara intrinsik jahat, tentu saja, kecuali jika itu tak terbatas dan panas. Saya pikir masalah yang lebih penting mungkin berhubungan dengan mengapa skrip perl gagal dan perlu dimulai kembali.
Craig
2
Lebih baik: sleep 1& /someperlscript.pl; wait.
user23013
2
@EliahKagan Terlihat dengan baik. TBH, saya tidak pernah menggunakan untilinstruksi, saya telah membingungkannya dengan do/untilloop hipotetis yang tidak ada di shell.
jlliagre
13

Jawaban lain, tentang menggunakan inotify, benar, tetapi bukan jawaban untuk pertanyaan ini.

Sebuah proses pengawas, seperti supervisord, upstart, atau runit, dirancang untuk persis masalah menonton dan restart layanan jika crash.

Distro Anda mungkin dilengkapi dengan pengawas proses bawaan.

Jacob Krall
sumber
11

while truebaik sebagai konstruksi "loop selamanya" tujuan umum. Seperti jawaban lain mengatakan, badan loop tidak boleh kosong, atau menjadi kosong berdasarkan perintah di dalam loop tidak berfungsi.

Jika Anda menggunakan Linux, Anda mungkin ingin menggunakan perintah like inotifywait, yang membuat whileloop lebih sederhana:

while inotifywait -qqe modify "$DIRECTORY"
do
    process_the_directory "$DIRECTORY"
done

Di sini, inotifywaitperintah akan duduk dan menunggu peristiwa sistem file terjadi (dalam contoh ini, ketika file dalam direktori ditulis ke). Pada titik itu, ia keluar dengan sukses dan tubuh loop dijalankan. Kemudian kembali menunggu lagi. Karena inotifywaitperintah menunggu sesuatu terjadi di direktori, itu jauh lebih efisien daripada terus-menerus polling direktori.

Stuart Caie
sumber
`(apt-get or yum) pasang inotify-tools` +1 keren!
JJoao
4

Pindahkan sementara 1 ke skrip perl (Mengikuti saran @roaima)

#!/usr/bin/perl

 use Linux::Inotify2;

 my $inotify = new Linux::Inotify2 or die "unable to inotify: $!";

 $inotify->watch ("Dir", IN_MODIFY, ## or in_{acess,create,open, etc...}
   sub { my $e = shift;
     my $name = $e->fullname;
     ## whatever 
     print "$name was modified\n" if $e->IN_MODIFY;
  });

 1 while $inotify->poll;
Joao
sumber
1
Ini tidak membahas persyaratan mulai ulang jika skrip macet atau terbunuh karena suatu alasan.
jlliagre
@ jlliagre, terima kasih atas komentarnya. Dalam jawabannya, saya tidak melihat spesifikasi yang jelas untuk perilaku yang dimaksud setelah terjadi crash atau kill. Ini jelas pertanyaan yang menarik dan relevan. Untuk saat ini saya akan membuatnya tetap sederhana (jika skrip itu mati atau terbunuh, tetap mati :)
JJoao
@ jlliagre Itulah pengecualian untuk. Tetapi Perl mungkin bukan pilihan yang paling tepat dalam kasus seperti itu karena tidak mendukung mekanisme pengecualian. Tebakan saja. Akan lebih baik untuk port script untuk bahasa yang mendukung pengecualian. Itu semua tergantung seberapa besar pekerjaan porting, tentu saja.
@Nasha, In Perl, dengan penangan sinyal, variabel kesalahan, Tyr :: Tiny, dll, kita bisa melakukannya! ... tapi - Aku benci penanganan pengecualian dan pemulihan kesalahan: mereka tidak pernah lengkap ...
JJoao
1
@ Jojo Saya setuju dengan Anda tentang fakta bahwa pemulihan kesalahan jarang selesai. Tentu saja tergantung pada pengembang untuk mencakup semua kasus yang mungkin. Begitu juga dengan PLC di dunia industri karena itu harus sangat mungkin ;-).
3

Ketika skrip perl Anda dimaksudkan untuk terus berjalan sepanjang waktu, mengapa menggunakan konstruksi while? Ketika perl gagal karena beberapa masalah serius, skrip perl baru yang dimulai saat itu mungkin macet sama kerasnya. Lagi-dan-lagi-dan-seterusnya.
jika Anda benar-benar ingin perl Anda mulai lagi, pertimbangkan crontab dan skrip yang pertama memeriksa untuk menjalankan instance. Dengan cara ini skrip Anda bahkan akan dimulai setelah reboot.

Walter A
sumber
2

Secara umum tidak ada masalah menggunakan while true karena ini adalah tes kecil yang hanya dieksekusi setelah skrip perl dihentikan. Perlu diingat bahwa tergantung pada varian Linux / Unix yang Anda gunakan, skrip mungkin akan dihentikan saat log off. Dalam kasus seperti itu pertimbangkan untuk menggunakan loop dalam skrip dan menyebutnya dengan nohupdan letakkan di latar belakang yaitunohup myscript &

Jika skrip perl berakhir terlalu sering dan menyebabkan beban CPU dari pada beban ini bertanggung jawab pada skrip perl dan bukan while true .

Lihat juga man nohupuntuk detailnya.

Lambert
sumber
Satu-satunya masalah adalah potensi loop panas yang mendorong pemanfaatan CPU. Jadi saya sangat setuju dengan meletakkan panggilan tidur di dalam loop untuk menjinakkannya.
Craig
2

Jika Anda ingin mengelola suatu proses, Anda mungkin ingin melihat ke manajer proses untuk melakukannya.

Dengan banyak sistem terbaru, Anda dapat menggunakan systemd untuk memantau proses Anda (yang merupakan salah satu manfaat dari systemd dibandingkan skrip init klasik). Jika distro pilihan Anda tidak menggunakan systemd, Anda bisa menggunakan daemontools atau monit .

Andrew Aylett
sumber
monadalah alternatif sederhana, jika pelukan monit dan systemd mengganggu Anda sebanyak yang saya lakukan.
Anko
2

The whileloop benar-benar bukan ide yang baik. Tidak ada jalan keluar di sana - itu hanya berjalan selamanya - secara statis . Sejumlah hal dapat berubah di lingkungan dan tidak akan terpengaruh - dan ini bisa buruk.

Sebagai contoh, jika shell executable yang bertanggung jawab untuk whileloop itu ditingkatkan, kernel tidak akan dapat melepaskan ruang disk untuk versi lama sampai skrip itu berhenti karena itu perlu mempertahankan deskriptor selama itu berjalan. Dan itu berlaku untuk semua file yang mungkin dibuka oleh shell untuk alasan apa pun untuk menjalankan loop - semuanya akan terbuka oleh whileloop ini selama dijalankan - selamanya.

Dan jika, Tuhan melarang, ada kebocoran memori di mana saja di shell menjalankan loop itu - bahkan paling banyak - itu hanya akan terus bocor - secara statis . Itu akan dibangun tanpa pengawasan dan satu-satunya jalan adalah dengan membunuhnya dengan paksa, lalu memulainya hanya untuk melakukan hal yang sama nanti.

Ini sebenarnya bukan cara Anda harus menyiapkan proses latar belakang - setidaknya, tidak menurut saya. Sebaliknya, seperti yang saya pikirkan, harus ada titik reset - penyegaran skrip dan kondisinya. Cara termudah untuk melakukannya adalah dengan exec. Anda dapat mengganti proses saat ini dengan yang baru - sambil mempertahankan PID yang sama - tetapi masih menjalankan yang baru proses .

Misalnya, jika perlskrip Anda harus mengembalikan true setelah berhasil menangani modifikasi file di beberapa direktori yang dipantau:

#!/bin/sh
trap 'rm -rf -- "${ldir%%*.}"' 0 INT
_exec() case    $#      in
        (0)     exec env -  "PID=$$" "ldir=${TMPDIR:-/tmp}/." \
                            "$0" "$@";;
        (*)     export "$@" "boff=0" "lmt=30"
                exec        "$0" "$@";;
        esac
[ "$PID" = "$$" ] || _exec
[ -w "$ldir" ]  &&
case    $ldir   in
(*.)    until   mkdir -- "$ldir"
        do      :& ldir=$ldir$$$!
        done    2>/dev/null
;;
(*)     until   /someperlscript.pl ||
                [ "$((boff+=1))"  -ge "$lmt" ]
        do      [ -d "$ldir" ]     &&
                sleep "$boff"      || ! break
        done
;;esac  &&      _exec ldir PID

...atau semacam itu. Sesuatu yang memungkinkan mesin dasar di belakang loop untuk menyegarkan sesekali.

mikeserv
sumber
1

Saya mengubah beberapa jawaban ini, tetapi secara naluriah saya memiliki perasaan paling hangat untuk jawaban @ WalterA. Yah, saya lakukan sampai saya membuat jawaban saya sendiri ...

Secara pribadi, saya cenderung memperbarui skrip Perl sehingga ia menulis entri log deskriptif tentang kegagalan dan mengirimkan peringatan ke administrator.

Jika skrip Perl dimaksudkan untuk terus berjalan, menunggu perubahan acara dari sistem file, mengapa gagal?

Jika tidak gagal, mengapa Anda khawatir tentang membungkusnya dalam skrip untuk memulai kembali tanpa batas?

Jika ada masalah konfigurasi atau ketergantungan yang rusak yang menyebabkan skrip Perl dibatalkan, cukup restart berulang-ulang tidak akan membuatnya tiba-tiba mulai berfungsi.

Anda tahu definisi kegilaan, bukan? (melakukan hal yang sama berulang-ulang, mengharapkan hasil yang berbeda). Hanya mengatakan. ;-)

Craig
sumber
haha- saya tahu, saya tahu. tapi tahukah Anda - dunia nyata menyebalkan. Bagaimanapun - alasannya adalah bahwa ia memproses file, dan beberapa file bahkan mungkin tidak dapat dikirim dengan benar. Kami belum tahu berbagai alasan mengapa itu bisa gagal. Saya mengerti maksud Anda tentang mencatat kesalahan, tetapi saya masih membutuhkannya untuk memproses file berikutnya melalui sesuatu seperti supervisord
Abhinav Gujjar
Jika Anda dapat menangkap pengecualian, Anda dapat mencatatnya lalu terus memproses, dan bahkan kembali dan sesekali mencoba kembali file yang gagal? Mungkin file yang gagal dikunci oleh pengguna lain atau proses ketika Anda mencoba memprosesnya, dll.
Craig