Saya menggunakan tail -f untuk memonitor file log yang sedang ditulis secara aktif. Ketika string tertentu ditulis ke file log, saya ingin keluar dari pemantauan, dan melanjutkan dengan sisa skrip saya.
Saat ini saya menggunakan:
tail -f logfile.log | grep -m 1 "Server Started"
Ketika string ditemukan, grep berhenti seperti yang diharapkan, tetapi saya perlu menemukan cara untuk membuat perintah tail juga berhenti sehingga skrip dapat melanjutkan.
tail
hanya di baris berikutnya. Coba ini:date > log; tail -f log | grep -m 1 trigger
dan kemudian di shell lain:echo trigger >> log
dan Anda akan melihat outputtrigger
di shell pertama, tetapi tidak ada penghentian perintah. Kemudian coba:date >> log
di shell kedua dan perintah di shell pertama akan berakhir. Namun terkadang ini sudah terlambat; kami ingin mengakhiri segera setelah garis pemicu muncul, bukan ketika garis setelah garis pemicu selesai.tail
+grep -q
seperti jawaban 00prometheusJawaban:
Satu-baris POSIX sederhana
Berikut ini adalah one-liner sederhana. Tidak perlu trik bash-spesifik atau non-POSIX, atau bahkan pipa bernama. Semua Anda benar-benar butuhkan adalah untuk memisahkan penghentian
tail
darigrep
. Dengan begitu, sekaligrep
berakhir, skrip dapat berlanjut meskipuntail
belum berakhir. Jadi metode sederhana ini akan membantu Anda:grep
akan memblokir sampai telah menemukan string, di mana ia akan keluar. Dengantail
menjalankan dari sub-shell sendiri, kita dapat menempatkannya di latar belakang sehingga berjalan secara independen. Sementara itu, shell utama bebas untuk melanjutkan eksekusi skrip segera setelahgrep
keluar.tail
akan berlama-lama di sub-shellnya sampai baris berikutnya telah ditulis ke logfile, dan kemudian keluar (mungkin bahkan setelah skrip utama dihentikan). Poin utama adalah bahwa pipa tidak lagi menunggu untuktail
berakhir, sehingga pipa keluar segera setelahgrep
keluar.Beberapa perubahan kecil:
tail
membuatnya mulai membaca dari baris terakhir logfile saat ini, seandainya string ada sebelumnya di logfile.tail
-F daripada -f. Ini bukan POSIX, tetapi memungkinkantail
untuk bekerja bahkan jika log diputar sambil menunggu.grep
berhenti setelah kejadian pertama, tetapi tanpa mencetak garis pemicu. Juga POSIX, yang -m1 tidak.sumber
tail
berjalan di latar belakang selamanya. Bagaimana Anda menangkaptail
PID dalam sub-shell berlatar belakang dan mengeksposnya shell utama? Saya hanya bisa membuat solusi sub-opsional dengan mematikan semuatail
proses sesi terlampir , menggunakanpkill -s 0 tail
.tail
akan berakhir segera setelah mencoba menulis ke pipa yang rusak. Pipa akan pecah segera setelahgrep
selesai, jadi setelahgrep
selesai,tail
akan berakhir setelah file log mendapat satu baris lagi di dalamnya.tail -f
.Jawaban yang diterima tidak berfungsi untuk saya, ditambah lagi membingungkan dan mengubah file log.
Saya menggunakan sesuatu seperti ini:
Jika baris log cocok dengan pola, bunuh
tail
mulai dengan skrip ini.Catatan: jika Anda ingin juga melihat output di layar, pilih salah satu
| tee /dev/tty
atau gema garis sebelum menguji di loop sementara.sumber
pkill
tidak ditentukan oleh POSIX dan tidak tersedia di mana-mana.Jika Anda menggunakan Bash (setidaknya, tetapi tampaknya itu tidak didefinisikan oleh POSIX, jadi mungkin ada beberapa shell yang hilang), Anda dapat menggunakan sintaksisnya
Ini berfungsi sangat mirip dengan solusi FIFO yang telah disebutkan, tetapi jauh lebih mudah untuk ditulis.
sumber
SIGTERM
(Ctrl + C, keluar dari perintah, atau membunuhnya)tail
akan membacanya, cobalah untuk output dan kemudian menerima SIGPIPE yang akan menghentikannya. Jadi, pada prinsipnya Anda benar; yangtail
mungkin berjalan tanpa batas jika tidak ada akan ditulis ke file log lagi. Dalam praktiknya ini mungkin solusi yang sangat rapi untuk banyak orang.Ada beberapa cara
tail
untuk keluar:Pendekatan yang Buruk: Memaksa
tail
untuk menulis baris lainAnda dapat memaksa
tail
untuk menulis baris output lain segera setelahgrep
menemukan kecocokan dan keluar. Ini akan menyebabkantail
untuk mendapatkanSIGPIPE
, menyebabkannya keluar. Salah satu cara untuk melakukan ini adalah memodifikasi file yang dipantautail
setelahgrep
keluar.Berikut ini beberapa contoh kode:
Dalam contoh ini,
cat
tidak akan keluar sampaigrep
stdout ditutup, jaditail
tidak mungkin untuk dapat menulis ke pipa sebelumgrep
memiliki kesempatan untuk menutup stdinnya.cat
digunakan untuk menyebarkan output standar yanggrep
tidak dimodifikasi.Pendekatan ini relatif sederhana, tetapi ada beberapa kelemahan:
grep
menutup stdout sebelum menutup stdin, akan selalu ada kondisi balapan:grep
menutup stdout, memicucat
untuk keluar, memicuecho
, memicutail
untuk menghasilkan garis. Jika baris ini dikirim kegrep
sebelumgrep
memiliki kesempatan untuk menutup stdin,tail
tidak akan mendapatkanSIGPIPE
sampai menulis baris lain.tail
— itu tidak akan bekerja dengan program lain.bash
'sPIPESTATUS
). Ini bukan masalah besar dalam kasus ini karenagrep
akan selalu mengembalikan 0, tetapi secara umum tahap tengah mungkin diganti dengan perintah berbeda yang kode pengembaliannya Anda pedulikan (misalnya, sesuatu yang mengembalikan 0 ketika "server mulai" terdeteksi, 1 ketika "server gagal memulai" terdeteksi).Pendekatan selanjutnya menghindari keterbatasan ini.
Pendekatan yang Lebih Baik: Hindari Saluran Pipa
Anda dapat menggunakan FIFO untuk menghindari pipa sama sekali, yang memungkinkan eksekusi berlanjut setelah
grep
kembali. Sebagai contoh:Garis yang ditandai dengan komentar
# optional
dapat dihapus dan program akan tetap bekerja;tail
hanya akan berlama-lama sampai membaca baris input lain atau terbunuh oleh proses lain.Keuntungan dari pendekatan ini adalah:
tail
grep
(atau perintah alternatif apa pun yang Anda gunakan)Kelemahan dari pendekatan ini adalah kompleksitas, terutama mengelola FIFO: Anda harus secara aman menghasilkan nama file sementara, dan Anda harus memastikan bahwa FIFO sementara dihapus bahkan jika pengguna menekan Ctrl-C di tengah-tengah naskah. Ini bisa dilakukan menggunakan perangkap.
Pendekatan Alternatif: Kirim Pesan ke Kill
tail
Anda bisa mendapatkan
tail
tahap pipa untuk keluar dengan mengirimkannya seperti sinyalSIGTERM
. Tantangannya adalah mengetahui dua hal di tempat yang sama dalam kode:tail
PID dan apakahgrep
telah keluar.Dengan pipeline like
tail -f ... | grep ...
, mudah untuk memodifikasi tahap pipeline pertama untuk menyimpantail
PID dalam sebuah variabel dengan latar belakangtail
dan membaca$!
. Juga mudah untuk memodifikasi tahap pipa kedua untuk dijalankankill
ketikagrep
keluar. Masalahnya adalah bahwa dua tahap pipa berjalan dalam "lingkungan eksekusi" yang terpisah (dalam terminologi standar POSIX) sehingga tahap pipa kedua tidak dapat membaca variabel yang ditetapkan oleh tahap pipa pertama. Tanpa menggunakan variabel shell, entah itu tahap kedua harus entah bagaimana mencari tahutail
PID sehingga bisa membunuhtail
ketikagrep
kembali, atau tahap pertama harus entah bagaimana diberitahu ketikagrep
kembali.Tahap kedua bisa digunakan
pgrep
untuk mendapatkantail
PID, tetapi itu tidak bisa diandalkan (Anda mungkin cocok dengan proses yang salah) dan non-portabel (pgrep
tidak ditentukan oleh standar POSIX).Tahap pertama bisa mengirim PID ke tahap kedua melalui pipa dengan
echo
memasukkan PID, tetapi string ini akan dicampur dengantail
output. Demultiplexing keduanya mungkin memerlukan skema pelarian yang kompleks, tergantung pada output daritail
.Anda dapat menggunakan FIFO agar tahap pipa kedua memberi tahu tahap pipa pertama saat
grep
keluar. Maka tahap pertama bisa membunuhtail
. Berikut ini beberapa contoh kode:Pendekatan ini memiliki semua pro dan kontra dari pendekatan sebelumnya, kecuali itu lebih rumit.
Peringatan Tentang Buffering
POSIX memungkinkan stdin dan stdout stream sepenuhnya buffered, yang berarti bahwa
tail
output mungkin tidak diproses olehgrep
untuk waktu yang lama secara sewenang-wenang. Seharusnya tidak ada masalah pada sistem GNU:grep
Penggunaan GNUread()
, yang menghindari semua buffering, dan GNUtail -f
melakukan panggilan rutinfflush()
ketika menulis ke stdout. Sistem non-GNU mungkin harus melakukan sesuatu yang khusus untuk menonaktifkan atau membersihkan buffer secara berkala.sumber
tail -f
akan menampilkan sepuluh baris terakhir, dan kemudian semua berikut. Untuk meningkatkan ini, Anda dapat menambahkan opsi-n 10000
ke ekor sehingga 10.000 baris terakhir diberikan juga.tail -f
melalui fifo dan grepping di atasnya:mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f
.tail -f log
menulis ke FIFO akan menyebabkan beberapa sistem (misalnya, GNU / Linux) menggunakan buffering berbasis blok alih-alih buffering berbasis garis, yang berartigrep
mungkin tidak melihat baris yang cocok ketika itu muncul di log. Sistem mungkin menyediakan utilitas untuk mengubah buffering, sepertistdbuf
dari GNU coreutils. Utilitas seperti itu akan menjadi non-portabel.grep -q -m 1 trigger <(tail -f log)
diusulkan lebih sederhana di tempat lain dan hidup dengan fakta bahwatail
berjalan satu baris lebih lama di latar belakang daripada yang dibutuhkan.Biarkan saya memperluas jawaban di @ 00prometheus (yang terbaik).
Mungkin Anda harus menggunakan batas waktu alih-alih menunggu tanpa batas waktu.
Fungsi bash di bawah ini akan memblokir hingga istilah pencarian yang diberikan muncul atau batas waktu yang diberikan tercapai.
Status keluar akan menjadi 0 jika string ditemukan dalam batas waktu.
Mungkin file log belum ada setelah meluncurkan server Anda. Dalam hal ini, Anda harus menunggu sampai muncul sebelum mencari string:
Inilah cara Anda dapat menggunakannya:
sumber
timeout
perintah?timeout
adalah satu-satunya cara yang dapat diandalkan untuk tidak menggantung tanpa batas menunggu server yang tidak dapat memulai dan telah keluar.Jadi setelah melakukan beberapa pengujian, saya menemukan cara 1-baris cepat untuk membuat pekerjaan ini. Tampaknya tail -f akan berhenti ketika grep berhenti, tetapi ada tangkapan. Tampaknya hanya dipicu jika file dibuka dan ditutup. Saya telah menyelesaikan ini dengan menambahkan string kosong ke file ketika grep menemukan kecocokan.
Saya tidak yakin mengapa buka / tutup file memicu ekor untuk menyadari bahwa pipa ditutup, jadi saya tidak akan bergantung pada perilaku ini. tetapi tampaknya berfungsi untuk saat ini.
Alasannya ditutup, lihat bendera -F, versus bendera -f.
sumber
tail
untuk mengeluarkan baris lain, tetapi pada saat itugrep
telah keluar (mungkin - ada kondisi balapan di sana). Jikagrep
sudah keluar saattail
menulis baris lain,tail
akan mendapatSIGPIPE
. Itu menyebabkantail
untuk segera keluar.tail
(6) Anda tidak dapat dengan mudah mengubah itu untuk berperilaku berbeda tergantung pada pencocokan string yang berbeda ("server mulai" vs "server mulai gagal") karena Anda tidak dapat dengan mudah mendapatkan kode pengembalian dari tahap tengah pipa. Ada pendekatan alternatif yang menghindari semua masalah ini - lihat jawaban saya.Saat ini, seperti yang diberikan, semua
tail -f
solusi di sini berisiko mengambil jalur "Server Dimulai" yang sebelumnya dicatat (yang mungkin atau mungkin tidak menjadi masalah dalam kasus spesifik Anda, tergantung pada jumlah baris yang dicatat dan rotasi file log / pemotongan).Daripada hal-hal yang terlalu rumit, gunakan saja yang lebih pintar
tail
, seperti yang ditunjukkan bmike dengan potongan perl. Solusi paling sederhana adalah iniretail
yang memiliki dukungan regex terintegrasi dengan pola kondisi mulai dan berhenti :Ini akan mengikuti file seperti biasa
tail -f
sampai instance baru pertama dari string itu muncul, kemudian keluar. (-u
Opsi tidak memicu pada baris yang ada di 10 baris terakhir file ketika dalam mode "follow" normal.)Jika Anda menggunakan GNU
tail
(dari coreutils ), opsi paling sederhana berikutnya adalah menggunakan--pid
dan FIFO (pipa bernama):FIFO digunakan karena proses harus dimulai secara terpisah untuk mendapatkan dan melewati PID. Sebuah FIFO masih mengalami masalah yang sama yaitu berkeliaran untuk penulisan yang tepat waktu menyebabkan
tail
menerima SIGPIPE , gunakan--pid
opsi sehinggatail
keluar ketika pemberitahuan itugrep
telah berakhir (biasanya digunakan untuk memantau proses penulis daripada pembaca , tetapitail
tidak t benar-benar peduli). Opsi-n 0
digunakan dengantail
sehingga garis lama tidak memicu kecocokan.Akhirnya, Anda bisa menggunakan ekor stateful , ini akan menyimpan offset file saat ini sehingga permintaan berikutnya hanya menampilkan baris baru (juga menangani rotasi file). Contoh ini menggunakan FWTK
retail
* lama :* Catatan, nama yang sama, program yang berbeda dengan opsi sebelumnya.
Daripada memiliki loop CPU-hogging, bandingkan timestamp file dengan file state (
.${LOGFILE}.off
), dan sleep. Gunakan "-T
" untuk menentukan lokasi file status jika diperlukan, yang di atas mengasumsikan direktori saat ini. Jangan ragu untuk melewati kondisi itu, atau di Linux Anda bisa menggunakan yang lebih efisieninotifywait
sebagai gantinya:sumber
retail
dengan batas waktu, seperti: "Jika 120 detik telah berlalu dan ritel masih belum membaca baris, maka berikan kode kesalahan dan keluar dari ritel"?timeout
(coreutils) untuk memulairetail
dan cukup periksa kode keluar 124 pada batas waktu (timeout
akan mematikan perintah apa pun yang Anda gunakan untuk memulai setelah waktu yang Anda tetapkan)Ini akan sedikit rumit karena Anda harus masuk ke kontrol proses dan pensinyalan. Lebih banyak kludgey akan menjadi solusi dua skrip menggunakan pelacakan PID. Lebih baik menggunakan pipa bernama seperti ini.
Script shell apa yang Anda gunakan?
Untuk solusi skrip cepat dan kotor, saya akan membuat skrip perl menggunakan File: Tail
Jadi daripada mencetak di dalam loop sementara, Anda bisa memfilter untuk kecocokan string dan keluar dari loop sementara untuk membiarkan skrip Anda berlanjut.
Salah satu dari ini harus melibatkan hanya sedikit pembelajaran untuk menerapkan kontrol aliran menonton yang Anda cari.
sumber
maxinterval=>300
berarti akan memeriksa file setiap lima menit. Karena saya tahu bahwa baris saya akan muncul dalam file untuk sementara waktu, saya menggunakan pemungutan suara yang jauh lebih agresif:maxinterval=>0.2, adjustafter=>10000
tunggu file muncul
tunggu string untuk muncul dalam file
https://superuser.com/a/743693/129669
sumber
/path/to/the.file
yang berukuran 1,4GB; maka jelas bahwa ini adalah masalah. 2. Ia menunggu lebih lama dari yang diperlukan ketika entri log telah muncul, dalam kasus terburuk 10s.Saya tidak bisa membayangkan solusi yang lebih bersih dari ini:
ok, mungkin namanya bisa diperbaiki ...
Keuntungan:
sumber
Anda tidak perlu perlu melakukan itu. Saya pikir perintah arloji adalah apa yang Anda cari. Perintah arloji memonitor output dari file dan dapat diakhiri dengan opsi -g ketika output berubah.
sumber
Alex saya pikir ini akan banyak membantu Anda.
perintah ini tidak akan pernah memberikan entri pada logfile tetapi akan diam-diam ...
sumber
logfile
kalau tidak, ini bisa menjadi waktu yang lama sebelumtail
menghasilkan garis lain dan mendeteksi yanggrep
telah mati (melaluiSIGPIPE
).Berikut ini adalah solusi yang jauh lebih baik yang tidak mengharuskan Anda menulis ke file log, yang sangat berbahaya atau bahkan tidak mungkin dalam beberapa kasus.
Saat ini hanya memiliki satu efek samping,
tail
proses akan tetap di latar belakang sampai baris berikutnya ditulis ke log.sumber
tail -n +0 -f
mulai dari awal file.tail -n 0 -f
mulai dari akhir file.myscript.sh: line 14: 7845 Terminated sh -c 'tail...
tail
proses tetap berjalan di latar belakang.Solusi lain di sini memiliki beberapa masalah:
Inilah yang saya buat dengan menggunakan kucing jantan sebagai contoh (hapus hash jika Anda ingin melihat log saat permulaannya):
sumber
The
tail
perintah dapat dilatarbelakangi dan pid yang menggema kegrep
subkulit. Dalamgrep
subkulit penangan perangkap pada EXIT dapat membunuhtail
perintah.sumber
Baca semuanya. tldr: memisahkan pemutusan ekor dari grep.
Dua bentuk yang paling nyaman adalah
dan jika Anda memiliki bash
Tetapi jika ekor yang duduk di latar belakang mengganggu Anda, ada cara yang lebih baik daripada fifo atau jawaban lain di sini. Membutuhkan bash.
Atau jika itu bukan ekor yang menghasilkan sesuatu,
sumber
Coba gunakan inotify (inotifywait)
Anda mengatur inotifywait untuk setiap perubahan file, kemudian periksa file dengan grep, jika tidak ditemukan jalankan kembali inotifywait, jika ditemukan keluar dari loop ... Aduh seperti itu
sumber
$!
; inotifywait -e MODIFY / tmp / found; kill -KILL - $ MYPIDAnda ingin pergi segera setelah garis ditulis, tetapi Anda juga ingin pergi setelah batas waktu:
sumber
bagaimana dengan ini:
sementara benar; lakukan jika [! -z $ (grep "myRegEx" myLog.log)]; lalu hancurkan; fi; selesai
sumber