Memantau file hingga string ditemukan

60

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.

Alex Hofsteede
sumber
Saya bertanya-tanya pada Sistem Operasi apa poster asli berjalan. Pada sistem Linux RHEL5, saya terkejut menemukan bahwa perintah ekor mati begitu perintah grep menemukan kecocokan dan keluar.
ZaSter
4
@ ZaSter: Mati tailhanya di baris berikutnya. Coba ini: date > log; tail -f log | grep -m 1 triggerdan kemudian di shell lain: echo trigger >> logdan Anda akan melihat output triggerdi shell pertama, tetapi tidak ada penghentian perintah. Kemudian coba: date >> logdi 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.
Alfe
Itu adalah penjelasan dan contoh yang sangat bagus, @Alfe.
ZaSter
1
solusi kuat satu garis yang elegan adalah menggunakan tail+ grep -qseperti jawaban 00prometheus
Trevor Boyd Smith

Jawaban:

37

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 taildari grep. Dengan begitu, sekali grepberakhir, skrip dapat berlanjut meskipun tailbelum berakhir. Jadi metode sederhana ini akan membantu Anda:

( tail -f -n0 logfile.log & ) | grep -q "Server Started"

grepakan memblokir sampai telah menemukan string, di mana ia akan keluar. Dengan tailmenjalankan dari sub-shell sendiri, kita dapat menempatkannya di latar belakang sehingga berjalan secara independen. Sementara itu, shell utama bebas untuk melanjutkan eksekusi skrip segera setelah grepkeluar. tailakan 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 untuk tailberakhir, sehingga pipa keluar segera setelah grepkeluar.

Beberapa perubahan kecil:

  • Opsi -n0 untuk tailmembuatnya mulai membaca dari baris terakhir logfile saat ini, seandainya string ada sebelumnya di logfile.
  • Anda mungkin ingin memberikan tail-F daripada -f. Ini bukan POSIX, tetapi memungkinkan tailuntuk bekerja bahkan jika log diputar sambil menunggu.
  • Opsi -q daripada -m1 membuat grepberhenti setelah kejadian pertama, tetapi tanpa mencetak garis pemicu. Juga POSIX, yang -m1 tidak.
00rometheus
sumber
3
Pendekatan ini akan membiarkan proses tailberjalan di latar belakang selamanya. Bagaimana Anda menangkap tailPID dalam sub-shell berlatar belakang dan mengeksposnya shell utama? Saya hanya bisa membuat solusi sub-opsional dengan mematikan semua tailproses sesi terlampir , menggunakan pkill -s 0 tail.
Rick van der Zwet
Dalam sebagian besar kasus penggunaan seharusnya tidak menjadi masalah. Alasan Anda melakukan ini sejak awal adalah karena Anda mengharapkan lebih banyak baris ditulis ke file log. tailakan berakhir segera setelah mencoba menulis ke pipa yang rusak. Pipa akan pecah segera setelah grepselesai, jadi setelah grepselesai, tailakan berakhir setelah file log mendapat satu baris lagi di dalamnya.
00rometheus
ketika saya menggunakan solusi ini saya tidak melatar belakangi tail -f.
Trevor Boyd Smith
2
@ Trevor Boyd Smith, ya, itu berfungsi di sebagian besar situasi, tetapi masalah OP adalah bahwa grep tidak akan selesai sampai tail berhenti, dan tail tidak akan berhenti sampai baris lain muncul di file log setelah grep telah berhenti (ketika tail mencoba untuk beri makan pipa yang rusak oleh grep ending). Jadi, kecuali jika Anda mengeklik latar belakang, skrip Anda tidak akan melanjutkan eksekusi hingga baris tambahan muncul di file log, alih-alih tepat pada baris yang ditangkap grep.
00prometheus
Re "trail tidak akan berhenti sampai baris lain muncul setelah [pola string yang diperlukan]": Itu sangat halus dan saya benar-benar melewatkannya. Saya tidak memperhatikan karena pola yang saya cari ada di tengah dan semuanya dicetak dengan cepat. (Sekali lagi perilaku yang Anda gambarkan sangat halus)
Trevor Boyd Smith
57

Jawaban yang diterima tidak berfungsi untuk saya, ditambah lagi membingungkan dan mengubah file log.

Saya menggunakan sesuatu seperti ini:

tail -f logfile.log | while read LOGLINE
do
   [[ "${LOGLINE}" == *"Server Started"* ]] && pkill -P $$ tail
done

Jika baris log cocok dengan pola, bunuh tailmulai dengan skrip ini.

Catatan: jika Anda ingin juga melihat output di layar, pilih salah satu | tee /dev/ttyatau gema garis sebelum menguji di loop sementara.

Rob Whelan
sumber
2
Ini berfungsi, tetapi pkilltidak ditentukan oleh POSIX dan tidak tersedia di mana-mana.
Richard Hansen
2
Anda tidak perlu loop sementara. gunakan arloji dengan opsi -g dan Anda dapat menggunakan perintah pkill jahat.
l1zard
@ l1zard Bisakah Anda menyempurnakannya? Bagaimana Anda menonton ekor file log sampai baris tertentu muncul? (Kurang penting, tapi saya juga penasaran ketika watch -g ditambahkan; Saya memiliki server Debian yang lebih baru dengan opsi itu, dan yang lama lainnya berbasis RHEL tanpa itu).
Rob Whelan
Tidak jelas bagi saya mengapa ekor bahkan dibutuhkan di sini. Sejauh yang saya pahami ini benar, pengguna ingin menjalankan perintah tertentu ketika kata kunci tertentu dalam file log muncul. Perintah yang diberikan di bawah ini menggunakan arloji melakukan tugas ini.
l1zard
Tidak cukup - memeriksa ketika string yang diberikan ditambahkan ke file log. Saya menggunakan ini untuk memeriksa ketika Tomcat atau JBoss sepenuhnya dimulai; mereka menulis "Server mulai" (atau serupa) setiap kali itu terjadi.
Rob Whelan
15

Jika Anda menggunakan Bash (setidaknya, tetapi tampaknya itu tidak didefinisikan oleh POSIX, jadi mungkin ada beberapa shell yang hilang), Anda dapat menggunakan sintaksisnya

grep -m 1 "Server Started" <(tail -f logfile.log)

Ini berfungsi sangat mirip dengan solusi FIFO yang telah disebutkan, tetapi jauh lebih mudah untuk ditulis.

petch
sumber
1
Ini bekerja, tetapi ekor masih berjalan sampai Anda mengirim SIGTERM(Ctrl + C, keluar dari perintah, atau membunuhnya)
mems
3
@mem, setiap baris tambahan dalam file log akan dilakukan. The tailakan membacanya, cobalah untuk output dan kemudian menerima SIGPIPE yang akan menghentikannya. Jadi, pada prinsipnya Anda benar; yang tailmungkin berjalan tanpa batas jika tidak ada akan ditulis ke file log lagi. Dalam praktiknya ini mungkin solusi yang sangat rapi untuk banyak orang.
Alfe
14

Ada beberapa cara tailuntuk keluar:

Pendekatan yang Buruk: Memaksa tailuntuk menulis baris lain

Anda dapat memaksa tailuntuk menulis baris output lain segera setelah grepmenemukan kecocokan dan keluar. Ini akan menyebabkan tailuntuk mendapatkan SIGPIPE, menyebabkannya keluar. Salah satu cara untuk melakukan ini adalah memodifikasi file yang dipantau tailsetelah grepkeluar.

Berikut ini beberapa contoh kode:

tail -f logfile.log | grep -m 1 "Server Started" | { cat; echo >>logfile.log; }

Dalam contoh ini, cattidak akan keluar sampai grepstdout ditutup, jadi tailtidak mungkin untuk dapat menulis ke pipa sebelum grepmemiliki kesempatan untuk menutup stdinnya. catdigunakan untuk menyebarkan output standar yang greptidak dimodifikasi.

Pendekatan ini relatif sederhana, tetapi ada beberapa kelemahan:

  • Jika grepmenutup stdout sebelum menutup stdin, akan selalu ada kondisi balapan: grepmenutup stdout, memicu catuntuk keluar, memicu echo, memicu tailuntuk menghasilkan garis. Jika baris ini dikirim ke grepsebelum grepmemiliki kesempatan untuk menutup stdin, tailtidak akan mendapatkan SIGPIPEsampai menulis baris lain.
  • Ini membutuhkan akses tulis ke file log.
  • Anda harus OK dengan memodifikasi file log.
  • Anda dapat merusak file log jika Anda kebetulan menulis pada saat yang sama dengan proses lain (penulisan mungkin disisipkan, menyebabkan baris baru muncul di tengah pesan log).
  • Pendekatan ini khusus untuk tail— itu tidak akan bekerja dengan program lain.
  • Tahap pipa ketiga menyulitkan untuk mendapatkan akses ke kode pengembalian tahap pipa kedua (kecuali jika Anda menggunakan ekstensi POSIX seperti array bash's PIPESTATUS). Ini bukan masalah besar dalam kasus ini karena grepakan 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 grepkembali. Sebagai contoh:

fifo=/tmp/tmpfifo.$$
mkfifo "${fifo}" || exit 1
tail -f logfile.log >${fifo} &
tailpid=$! # optional
grep -m 1 "Server Started" "${fifo}"
kill "${tailpid}" # optional
rm "${fifo}"

Garis yang ditandai dengan komentar # optionaldapat dihapus dan program akan tetap bekerja; tailhanya akan berlama-lama sampai membaca baris input lain atau terbunuh oleh proses lain.

Keuntungan dari pendekatan ini adalah:

  • Anda tidak perlu memodifikasi file log
  • selain itu pendekatan ini berfungsi untuk utilitas lain tail
  • itu tidak menderita dari kondisi balapan
  • Anda dapat dengan mudah mendapatkan nilai kembali 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 tailtahap pipa untuk keluar dengan mengirimkannya seperti sinyal SIGTERM. Tantangannya adalah mengetahui dua hal di tempat yang sama dalam kode: tailPID dan apakah greptelah keluar.

Dengan pipeline like tail -f ... | grep ..., mudah untuk memodifikasi tahap pipeline pertama untuk menyimpan tailPID dalam sebuah variabel dengan latar belakang taildan membaca $!. Juga mudah untuk memodifikasi tahap pipa kedua untuk dijalankan killketika grepkeluar. 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 tahu tailPID sehingga bisa membunuh tailketika grepkembali, atau tahap pertama harus entah bagaimana diberitahu ketika grepkembali.

Tahap kedua bisa digunakan pgrepuntuk mendapatkan tailPID, tetapi itu tidak bisa diandalkan (Anda mungkin cocok dengan proses yang salah) dan non-portabel ( pgreptidak ditentukan oleh standar POSIX).

Tahap pertama bisa mengirim PID ke tahap kedua melalui pipa dengan echomemasukkan PID, tetapi string ini akan dicampur dengan tailoutput. Demultiplexing keduanya mungkin memerlukan skema pelarian yang kompleks, tergantung pada output dari tail.

Anda dapat menggunakan FIFO agar tahap pipa kedua memberi tahu tahap pipa pertama saat grepkeluar. Maka tahap pertama bisa membunuh tail. Berikut ini beberapa contoh kode:

fifo=/tmp/notifyfifo.$$
mkfifo "${fifo}" || exit 1
{
    # run tail in the background so that the shell can
    # kill tail when notified that grep has exited
    tail -f logfile.log &
    # remember tail's PID
    tailpid=$!
    # wait for notification that grep has exited
    read foo <${fifo}
    # grep has exited, time to go
    kill "${tailpid}"
} | {
    grep -m 1 "Server Started"
    # notify the first pipeline stage that grep is done
    echo >${fifo}
}
# clean up
rm "${fifo}"

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 tailoutput mungkin tidak diproses oleh grepuntuk waktu yang lama secara sewenang-wenang. Seharusnya tidak ada masalah pada sistem GNU: grepPenggunaan GNU read(), yang menghindari semua buffering, dan GNU tail -fmelakukan panggilan rutin fflush()ketika menulis ke stdout. Sistem non-GNU mungkin harus melakukan sesuatu yang khusus untuk menonaktifkan atau membersihkan buffer secara berkala.

Richard Hansen
sumber
Solusi Anda (seperti yang lain, saya tidak akan menyalahkan Anda) akan kehilangan hal-hal yang sudah ditulis ke file log sebelum pemantauan Anda dimulai. Hanya tail -fakan menampilkan sepuluh baris terakhir, dan kemudian semua berikut. Untuk meningkatkan ini, Anda dapat menambahkan opsi -n 10000ke ekor sehingga 10.000 baris terakhir diberikan juga.
Alfe
Ide lain: solusi fifo Anda dapat diluruskan, saya pikir, dengan melewati output dari tail -fmelalui fifo dan grepping di atasnya: mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f.
Alfe
@Alfe: Saya bisa saja salah, tetapi saya percaya memiliki tail -f logmenulis ke FIFO akan menyebabkan beberapa sistem (misalnya, GNU / Linux) menggunakan buffering berbasis blok alih-alih buffering berbasis garis, yang berarti grepmungkin tidak melihat baris yang cocok ketika itu muncul di log. Sistem mungkin menyediakan utilitas untuk mengubah buffering, seperti stdbufdari GNU coreutils. Utilitas seperti itu akan menjadi non-portabel.
Richard Hansen
1
@Alfe: Sebenarnya, sepertinya POSIX tidak mengatakan apa-apa tentang buffering kecuali ketika berinteraksi dengan terminal, jadi dari sudut pandang standar saya pikir solusi sederhana Anda sebaik solusi kompleks saya. Saya tidak 100% yakin tentang bagaimana berbagai implementasi sebenarnya berperilaku dalam setiap kasus, namun.
Richard Hansen
Sebenarnya, saya sekarang pergi untuk grep -q -m 1 trigger <(tail -f log)diusulkan lebih sederhana di tempat lain dan hidup dengan fakta bahwa tailberjalan satu baris lebih lama di latar belakang daripada yang dibutuhkan.
Alfe
8

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.

wait_str() {
  local file="$1"; shift
  local search_term="$1"; shift
  local wait_time="${1:-5m}"; shift # 5 minutes as default timeout

  (timeout $wait_time tail -F -n0 "$file" &) | grep -q "$search_term" && return 0

  echo "Timeout of $wait_time reached. Unable to find '$search_term' in '$file'"
  return 1
}

Mungkin file log belum ada setelah meluncurkan server Anda. Dalam hal ini, Anda harus menunggu sampai muncul sebelum mencari string:

wait_server() {
  echo "Waiting for server..."
  local server_log="$1"; shift
  local wait_time="$1"; shift

  wait_file "$server_log" 10 || { echo "Server log file missing: '$server_log'"; return 1; }

  wait_str "$server_log" "Server Started" "$wait_time"
}

wait_file() {
  local file="$1"; shift
  local wait_seconds="${1:-10}"; shift # 10 seconds as default timeout

  until test $((wait_seconds--)) -eq 0 -o -f "$file" ; do sleep 1; done

  ((++wait_seconds))
}

Inilah cara Anda dapat menggunakannya:

wait_server "/var/log/server.log" 5m && \
echo -e "\n-------------------------- Server READY --------------------------\n"
Elifarley
sumber
Jadi, di mana timeoutperintah?
ayanamist
Sebenarnya, menggunakan timeoutadalah satu-satunya cara yang dapat diandalkan untuk tidak menggantung tanpa batas menunggu server yang tidak dapat memulai dan telah keluar.
gluk47
1
Jawaban ini adalah yang terbaik. Cukup salin fungsi dan sebut saja, ini sangat mudah dan dapat digunakan kembali
Hristo Vrigazov
6

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.

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> logfile \;

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.

Alex Hofsteede
sumber
1
Ini berfungsi karena menambahkan ke logfile menyebabkan tailuntuk mengeluarkan baris lain, tetapi pada saat itu greptelah keluar (mungkin - ada kondisi balapan di sana). Jika grepsudah keluar saat tailmenulis baris lain, tailakan mendapat SIGPIPE. Itu menyebabkan tailuntuk segera keluar.
Richard Hansen
1
Kerugian dari pendekatan ini: (1) ada kondisi balapan (mungkin tidak selalu keluar segera) (2) itu memerlukan akses tulis ke file log (3) Anda harus OK dengan memodifikasi file log (4) Anda dapat merusak file log (5) ini hanya berfungsi untuk 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.
Richard Hansen
6

Saat ini, seperti yang diberikan, semua tail -fsolusi 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 ini retailyang memiliki dukungan regex terintegrasi dengan pola kondisi mulai dan berhenti :

retail -f -u "Server Started" server.log > /dev/null

Ini akan mengikuti file seperti biasa tail -fsampai instance baru pertama dari string itu muncul, kemudian keluar. ( -uOpsi 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 --piddan FIFO (pipa bernama):

mkfifo ${FIFO:=serverlog.fifo.$$}
grep -q -m 1 "Server Started" ${FIFO}  &
tail -n 0 -f server.log  --pid $! >> ${FIFO}
rm ${FIFO}

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 tailmenerima SIGPIPE , gunakan --pidopsi sehingga tailkeluar ketika pemberitahuan itu greptelah berakhir (biasanya digunakan untuk memantau proses penulis daripada pembaca , tetapi tailtidak t benar-benar peduli). Opsi -n 0digunakan dengan tailsehingga 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 :

retail "${LOGFILE:=server.log}" > /dev/null   # skip over current content
while true; do
    [ "${LOGFILE}" -nt ".${LOGFILE}.off" ] && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
    sleep 2
done

* 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 efisien inotifywaitsebagai gantinya:

retail "${LOGFILE:=server.log}" > /dev/null
while true; do
    inotifywait -qq "${LOGFILE}" && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
done
mr.spuratic
sumber
Dapatkah saya menggabungkan retaildengan batas waktu, seperti: "Jika 120 detik telah berlalu dan ritel masih belum membaca baris, maka berikan kode kesalahan dan keluar dari ritel"?
kiltek
@kiltek gunakan GNU timeout(coreutils) untuk memulai retaildan cukup periksa kode keluar 124 pada batas waktu ( timeoutakan mematikan perintah apa pun yang Anda gunakan untuk memulai setelah waktu yang Anda tetapkan)
mr.spuratic
4

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

use File::Tail;
$file=File::Tail->new(name=>$name, maxinterval=>300, adjustafter=>7);
while (defined($line=$file->read)) {
    last if $line =~ /Server started/;
}

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.

bmike
sumber
menggunakan bash. perl-fu saya tidak begitu kuat, tapi saya akan mencoba ini.
Alex Hofsteede
Gunakan pipa - mereka suka bash dan bash mencintainya. (dan perangkat lunak cadangan Anda akan menghormati Anda ketika mengenai salah satu pipa Anda)
bmike
maxinterval=>300berarti 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
Stephen Ostermiller
2

tunggu file muncul

while [ ! -f /path/to/the.file ] 
do sleep 2; done

tunggu string untuk muncul dalam file

while ! grep "the line you're searching for" /path/to/the.file  
do sleep 10; done

https://superuser.com/a/743693/129669

Mykhaylo Adamovych
sumber
2
Polling ini memiliki dua kelemahan utama: 1. Memboroskan waktu perhitungan dengan menelusuri log lagi dan lagi. Pertimbangkan /path/to/the.fileyang 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.
Alfe
2

Saya tidak bisa membayangkan solusi yang lebih bersih dari ini:

#!/usr/bin/env bash
# file : untail.sh
# usage: untail.sh logfile.log "Server Started"
(echo $BASHPID; tail -f $1) | while read LINE ; do
    if [ -z $TPID ]; then
        TPID=$LINE # the first line is used to store the previous subshell PID
    else
        echo "$LINE"; [[ "$LINE" == *"${*:2}"* ]] && kill -3 $TPID && break
    fi
done

ok, mungkin namanya bisa diperbaiki ...

Keuntungan:

  • itu tidak menggunakan utilitas khusus
  • itu tidak menulis ke disk
  • itu dengan anggun berhenti ekor dan menutup pipa
  • itu sangat singkat dan mudah dimengerti
Giancarlo Sportelli
sumber
2

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.

watch -g grep -m 1 "Server Started" logfile.log && Yournextaction
lzard
sumber
1
Karena ini berjalan setiap dua detik, itu tidak langsung keluar begitu garis muncul di file log. Juga, itu tidak berfungsi dengan baik jika file log sangat besar.
Richard Hansen
1

Alex saya pikir ini akan banyak membantu Anda.

tail -f logfile |grep -m 1 "Server Started" | xargs echo "" >> /dev/null ;

perintah ini tidak akan pernah memberikan entri pada logfile tetapi akan diam-diam ...

Md. Mohsin Ali
sumber
1
Ini tidak akan berhasil - Anda harus menambahkan logfilekalau tidak, ini bisa menjadi waktu yang lama sebelum tailmenghasilkan garis lain dan mendeteksi yang greptelah mati (melalui SIGPIPE).
Richard Hansen
1

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.

sh -c 'tail -n +0 -f /tmp/foo | { sed "/EOF/ q" && kill $$ ;}'

Saat ini hanya memiliki satu efek samping, tailproses akan tetap di latar belakang sampai baris berikutnya ditulis ke log.

Sorin
sumber
tail -n +0 -fmulai dari awal file. tail -n 0 -fmulai dari akhir file.
Stephen Ostermiller
1
Efek samping lain yang saya dapatkan:myscript.sh: line 14: 7845 Terminated sh -c 'tail...
Stephen Ostermiller
Saya percaya bahwa "daftar berikutnya" harus "baris berikutnya" dalam jawaban ini.
Stephen Ostermiller
Ini berfungsi, tetapi tailproses tetap berjalan di latar belakang.
cbaldan
1

Solusi lain di sini memiliki beberapa masalah:

  • jika proses logging sudah turun atau turun selama loop mereka akan berjalan tanpa batas
  • mengedit log yang seharusnya hanya dilihat
  • tidak perlu menulis file tambahan
  • tidak memungkinkan untuk logika tambahan

Inilah yang saya buat dengan menggunakan kucing jantan sebagai contoh (hapus hash jika Anda ingin melihat log saat permulaannya):

function startTomcat {
    loggingProcessStartCommand="${CATALINA_HOME}/bin/startup.sh"
    loggingProcessOwner="root"
    loggingProcessCommandLinePattern="${JAVA_HOME}"
    logSearchString="org.apache.catalina.startup.Catalina.start Server startup"
    logFile="${CATALINA_BASE}/log/catalina.out"

    lineNumber="$(( $(wc -l "${logFile}" | awk '{print $1}') + 1 ))"
    ${loggingProcessStartCommand}
    while [[ -z "$(sed -n "${lineNumber}p" "${logFile}" | grep "${logSearchString}")" ]]; do
        [[ -z "$(ps -ef | grep "^${loggingProcessOwner} .* ${loggingProcessCommandLinePattern}" | grep -v grep)" ]] && { echo "[ERROR] Tomcat failed to start"; return 1; }
        [[ $(wc -l "${logFile}" | awk '{print $1}') -lt ${lineNumber} ]] && continue
        #sed -n "${lineNumber}p" "${logFile}"
        let lineNumber++
    done
    #sed -n "${lineNumber}p" "${logFile}"
    echo "[INFO] Tomcat has started"
}
pengguna503391
sumber
1

The tailperintah dapat dilatarbelakangi dan pid yang menggema ke grepsubkulit. Dalam grepsubkulit penangan perangkap pada EXIT dapat membunuh tailperintah.

( (sleep 1; exec tail -f logfile.log) & echo $! ; wait ) | 
     (trap 'kill "$pid"' EXIT; pid="$(head -1)"; grep -m 1 "Server Started")
phio
sumber
1

Baca semuanya. tldr: memisahkan pemutusan ekor dari grep.

Dua bentuk yang paling nyaman adalah

( tail -f logfile.log & ) | grep -q "Server Started"

dan jika Anda memiliki bash

grep -m 1 "Server Started" <(tail -f logfile.log)

Tetapi jika ekor yang duduk di latar belakang mengganggu Anda, ada cara yang lebih baik daripada fifo atau jawaban lain di sini. Membutuhkan bash.

coproc grep -m 1 "Server Started"
tail -F /tmp/x --pid $COPROC_PID >&${COPROC[1]}

Atau jika itu bukan ekor yang menghasilkan sesuatu,

coproc command that outputs
grep -m 1 "Sever Started" ${COPROC[0]}
kill $COPROC_PID
Ian Kelling
sumber
0

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

Evengard
sumber
Dengan cara ini, seluruh file harus diperiksa ulang setiap kali ada sesuatu yang ditulis padanya. Tidak berfungsi dengan baik untuk file log.
grawity
1
Cara lain adalah membuat dua skrip: 1. tail -f logfile.log | grep -m 1 "Server Dimulai"> / tmp / found 2. firstscript.sh & MYPID = $!; inotifywait -e MODIFY / tmp / found; kill -KILL - $ MYPID
Evengard
Saya ingin Anda mengedit jawaban Anda untuk menunjukkan menangkap PID dan kemudian menggunakan inotifywait - solusi elegan yang akan mudah dipahami bagi seseorang yang terbiasa dengan grep tetapi membutuhkan alat yang lebih canggih.
sepeda
PID dari apa yang ingin Anda tangkap? Saya dapat mencoba membuatnya jika Anda menjelaskan sedikit lebih banyak tentang apa yang Anda inginkan
Evengard
0

Anda ingin pergi segera setelah garis ditulis, tetapi Anda juga ingin pergi setelah batas waktu:

if (timeout 15s tail -F -n0 "stdout.log" &) | grep -q "The string that says the startup is successful" ; then
    echo "Application started with success."
else
    echo "Startup failed."
    tail stderr.log stdout.log
    exit 1
fi
Adrien
sumber
-2

bagaimana dengan ini:

sementara benar; lakukan jika [! -z $ (grep "myRegEx" myLog.log)]; lalu hancurkan; fi; selesai

Ather
sumber