स्ट्रिंग मिलने तक फ़ाइल की निगरानी करना


60

मैं एक लॉग फ़ाइल को सक्रिय करने के लिए पूंछ -f का उपयोग कर रहा हूं जिसे सक्रिय रूप से लिखा जा रहा है। जब एक निश्चित स्ट्रिंग लॉग फ़ाइल में लिखी जाती है, तो मैं निगरानी छोड़ना चाहता हूं, और मेरी स्क्रिप्ट के बाकी हिस्सों के साथ जारी रखना चाहता हूं।

वर्तमान में मैं उपयोग कर रहा हूं:

tail -f logfile.log | grep -m 1 "Server Started"

जब स्ट्रिंग मिल जाती है, तो grep अपेक्षित रूप से समाप्त हो जाती है, लेकिन मुझे टेल कमांड को छोड़ने का एक तरीका खोजने की आवश्यकता है ताकि स्क्रिप्ट जारी रह सके।


मुझे आश्चर्य है कि मूल पोस्टर किस ऑपरेटिंग सिस्टम पर चल रहा था। लिनक्स आरएचईएल 5 सिस्टम पर, मुझे यह जानकर आश्चर्य हुआ कि एक बार grep कमांड मैच देखने और बाहर निकलने पर टेल कमांड केवल मर जाता है।
ज़ास्टर

4
@ZaSter: tailअगली पंक्ति में ही मर जाता है। इसे आज़माएँ: date > log; tail -f log | grep -m 1 triggerऔर फिर दूसरे शेल में: echo trigger >> logऔर आप triggerपहले शेल में आउटपुट देखेंगे , लेकिन कमांड का कोई टर्मिनेशन नहीं। फिर प्रयास करें: date >> logदूसरे शेल में और पहले शेल में कमांड समाप्त हो जाएगी। लेकिन कभी-कभी यह बहुत देर हो जाती है; हम ट्रिगर लाइन दिखाई देते ही समाप्त करना चाहते हैं, जब ट्रिगर लाइन पूरी होने के बाद लाइन पूरी नहीं होती है।
एल्फ

यह एक उत्कृष्ट व्याख्या और उदाहरण है, @ शेल्फ।
ज़ास्टर

1
सुरुचिपूर्ण एक-लाइन मजबूत समाधान का उपयोग करना है tail+ grep -q00prometheus के उत्तर की तरह
ट्रेवर बोयड स्मिथ

जवाबों:


41

एक साधारण POSIX वन-लाइनर

यहां एक साधारण एक-लाइनर है। इसके लिए bash-specific या non-POSIX ट्रिक्स, या यहां तक ​​कि एक नामित पाइप की आवश्यकता नहीं है। आपको वास्तव में tailसे समाप्त करने की आवश्यकता है grep। इस तरह, एक बार grepसमाप्त होने पर, स्क्रिप्ट अभी भी tailसमाप्त नहीं होने पर भी जारी रह सकती है। तो यह सरल विधि आपको वहां मिलेगी:

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

grepतब तक ब्लॉक रहेगा जब तक कि उसे स्ट्रिंग नहीं मिल जाती है, जहां से वह बाहर निकल जाएगा। tailइसे स्वयं के उप-खोल से चलाने के द्वारा , हम इसे पृष्ठभूमि में रख सकते हैं, इसलिए यह स्वतंत्र रूप से चलता है। इस बीच, मुख्य खोल grepबाहर निकलते ही स्क्रिप्ट के निष्पादन को जारी रखने के लिए स्वतंत्र है। tailअगली पंक्ति के लॉगफ़ाइल को लिखे जाने तक अपने उप-खोल में घूमेंगे, और फिर बाहर निकलें (संभवतः मुख्य स्क्रिप्ट समाप्त होने के बाद भी)। मुख्य बिंदु यह है कि पाइपलाइन अब tailसमाप्त होने का इंतजार नहीं करती है , इसलिए पाइप लाइन से बाहर निकलते ही समाप्त हो जाती grepहै।

कुछ मामूली मोड़:

  • विकल्प -n0 tailयह लॉगफ़ाइल की वर्तमान अंतिम पंक्ति से पढ़ना शुरू करता है, अगर स्ट्रिंग लॉगफ़ाइल में पहले से मौजूद है।
  • आप tail-f के बजाय -F देना चाह सकते हैं । यह POSIX नहीं है, लेकिन यह tailतब भी काम करने की अनुमति देता है, जबकि प्रतीक्षा करते समय लॉग को घुमाया जाता है।
  • विकल्प -q बजाय -m1 grepपहली घटना के बाद छोड़ देता है, लेकिन ट्रिगर लाइन को प्रिंट किए बिना। इसके अलावा, यह POSIX है, जो -m1 नहीं है।

3
यह एप्रोच tailबैकग्राउंड में रनिंग को हमेशा के लिए छोड़ देगा । आप tailपृष्ठभूमि के उप-शेल के भीतर पीआईडी को कैसे कैप्चर करेंगे और इसे मुख्य शेल को उजागर करेंगे? मैं केवल सभी सत्र संलग्न tailप्रक्रियाओं को मारकर, उप-वैकल्पिक वैकल्पिक हल के साथ आ सकता हूं pkill -s 0 tail
रिक वैन डेर ज़्वेट

1
ज्यादातर उपयोग के मामलों में यह एक समस्या नहीं होनी चाहिए। आप इसे पहली जगह में कर रहे हैं इसका कारण यह है कि आप लॉग फ़ाइल में अधिक पंक्तियों के लिखे जाने की अपेक्षा कर रहे हैं। tailजैसे ही यह टूटी हुई पाइप को लिखने की कोशिश करता है, समाप्त हो जाएगा। जैसे ही grepपूरा हो जाएगा, पाइप टूट जाएगा , इसलिए एक बार grepपूरा tailहो जाने के बाद, लॉग फ़ाइल को इसमें एक और लाइन मिलने के बाद समाप्त कर देगा।
00prometheus

जब मैंने इस समाधान का उपयोग किया तो मैंने पृष्ठभूमि को नहीं देखाtail -f
ट्रेवर बॉयड स्मिथ

2
@ ट्रेवर बॉयड स्मिथ, हाँ, यह ज्यादातर स्थितियों में काम करता है, लेकिन ओपी समस्या यह थी कि टेल क्विट होने तक ग्रीप पूरा नहीं होगा, और ग्रिप छोड़ने के बाद लॉग फ़ाइल में एक और लाइन दिखाई देने तक टेल नहीं चलेगी (जब टेल करने की कोशिश करता है पाइप लाइन समाप्त होने से टूटी हुई पाइप लाइन को खिलाएं)। इसलिए जब तक आप बैकग्राउंड टेल नहीं करते हैं, तब तक आपकी स्क्रिप्ट निष्पादन को जारी नहीं रखेगी, जब तक कि लॉग फ़ाइल में एक अतिरिक्त लाइन दिखाई नहीं देती है, बल्कि उस रेखा पर होती है जो कि ग्रिप पकड़ता है।
00prometheus

पुनः "निशान तब तक नहीं छोड़ेगा जब तक [आवश्यक स्ट्रिंग पैटर्न] के बाद एक और लाइन दिखाई न दे।": यह बहुत ही सूक्ष्मता से है और मैं पूरी तरह से चूक गया। मैंने ध्यान नहीं दिया क्योंकि मैं जिस पैटर्न की तलाश कर रहा था वह बीच में था और यह सब तेजी से प्रिंट हो गया। (फिर से आपके द्वारा वर्णित व्यवहार बहुत सूक्ष्म है)
ट्रेवर बॉयड स्मिथ

59

स्वीकृत उत्तर मेरे लिए काम नहीं कर रहा है, साथ ही यह भ्रमित है और यह लॉग फ़ाइल को बदलता है।

मैं कुछ इस तरह का उपयोग कर रहा हूँ:

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

यदि लॉग लाइन पैटर्न से मेल खाती है, तो tailइस स्क्रिप्ट द्वारा शुरू की गई हत्या करें ।

नोट: यदि आप स्क्रीन पर आउटपुट देखना चाहते हैं, तो | tee /dev/ttyया तो लूप में परीक्षण से पहले लाइन को प्रतिध्वनित करें।


2
यह काम करता है, लेकिन pkillPOSIX द्वारा निर्दिष्ट नहीं है और यह हर जगह उपलब्ध नहीं है।
रिचर्ड हैनसेन

2
आप एक समय पाश की जरूरत नहीं है। -g विकल्प के साथ घड़ी का उपयोग करें और आप अपने आप को बुरा pkill आदेश छोड़ सकते हैं।
l1zard

@ l1zard क्या आप उस मांस को बाहर निकाल सकते हैं? जब तक एक विशेष लाइन दिखाई नहीं देती तब तक आप लॉग फ़ाइल की पूंछ कैसे देखेंगे? (कम महत्वपूर्ण है, लेकिन मैं उत्सुक हूं जब वॉच-जी को जोड़ा गया था; मेरे पास उस विकल्प के साथ एक नया डेबियन सर्वर है, और इसके बिना एक और पुराना आरएचईएल-आधारित है)।
रॉब व्हेलन

यह मेरे लिए बिल्कुल स्पष्ट नहीं है कि यहां पूंछ की आवश्यकता क्यों है। जहां तक ​​मैं समझता हूं यह सही है कि उपयोगकर्ता एक विशिष्ट कमांड निष्पादित करना चाहता है जब एक लॉग फ़ाइल में एक निश्चित कीवर्ड दिखाई देता है। घड़ी का उपयोग करके नीचे दी गई कमांड यह बहुत ही काम करती है।
l1zard

काफी नहीं है - जब एक दिया स्ट्रिंग है उसकी जांच करने की है जोड़ा लॉग फ़ाइल के लिए। मैं इसका उपयोग जाँच के लिए करता हूं जब टॉमकैट या जेबॉस पूरी तरह से शुरू हो जाता है; वे हर बार होने वाले "सर्वर शुरू" (या समान) लिखते हैं।
रोब व्हेलन

16

यदि आप बैश का उपयोग कर रहे हैं (कम से कम, लेकिन ऐसा लगता है कि यह POSIX द्वारा परिभाषित नहीं है, इसलिए यह कुछ गोले में गायब हो सकता है), आप सिंटैक्स का उपयोग कर सकते हैं

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

यह एफआईएफओ समाधानों की तरह ही बहुत काम करता है, लेकिन लिखने के लिए बहुत सरल है।


1
यह काम करता है, लेकिन पूंछ अभी भी आप तक चल रहा है एक भेजने SIGTERM(Ctrl + C, बाहर निकलें आदेश, या यह मारने)
एमईएमएस

3
@, लॉग फ़ाइल में कोई भी अतिरिक्त पंक्ति करेगी। tailयह पढ़ा जाएगा, उत्पादन के लिए यह कोशिश करते हैं और फिर एक SIGPIPE जो इसे समाप्त कर देगा प्राप्त करते हैं। तो, सिद्धांत रूप में आप सही हैं; tailअगर अनिश्चित काल तक लॉग फ़ाइल में कुछ भी दोबारा नहीं लिखा जाता है, तो अनिश्चित काल तक चलाया जा सकता है। व्यवहार में यह बहुत सारे लोगों के लिए एक बहुत साफ समाधान हो सकता है।
अल्फ

14

tailबाहर निकलने के लिए कुछ तरीके हैं :

खराब दृष्टिकोण: tailएक और पंक्ति लिखने के लिए बाध्य करना

आप एक मिलान और बाहर निकलने के tailतुरंत बाद आउटपुट की एक और पंक्ति लिखने के लिए बाध्य कर सकते हैं grep। यह tailएक प्राप्त करने का कारण होगा SIGPIPE, जिससे यह बाहर निकल जाएगा। ऐसा करने का एक तरीका यह है कि बाहर निकलने के tailबाद निगरानी की जा रही फ़ाइल को संशोधित करें grep

यहाँ कुछ उदाहरण कोड है:

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

इस उदाहरण में, catतब तक बाहर नहीं निकलेंगे, जब तक grepअपना स्टडआउट बंद नहीं कर दिया tailजाता , इसलिए पाइप को लिखने में सक्षम होने की संभावना नहीं है , इससे पहले grepकि इसकी स्टड को बंद करने का मौका मिला हो। catका उपयोग अनमॉडिफाइड के मानक उत्पादन को फैलाने के लिए किया जाता है grep

यह दृष्टिकोण अपेक्षाकृत सरल है, लेकिन कई डाउनसाइड हैं:

  • यदि grepस्टड बंद करने से पहले स्टडआउट बंद हो जाता है, तो हमेशा एक दौड़ की स्थिति होगी: grepस्टडआउट को बंद कर देता है, ट्रिगर catकरने के लिए बाहर निकलता है, ट्रिगर करता है echo, tailएक लाइन को आउटपुट करने के लिए ट्रिगर करता है। इस लाइन के लिए भेजा जाता है, तो grepइससे पहले कि grepstdin बंद करने के लिए, का अवसर मिल सके tailनहीं मिलेगा SIGPIPEजब तक यह एक और लाइन लिखता है।
  • इसके लिए लॉग फ़ाइल में लिखने की पहुँच आवश्यक है।
  • आपको लॉग फ़ाइल को संशोधित करने के साथ ठीक होना चाहिए।
  • यदि आप किसी अन्य प्रक्रिया के रूप में एक ही समय में लिखने के लिए होते हैं (लॉग्स बीच में हो सकता है, जिससे लॉगलाइन संदेश के बीच में एक नई पंक्ति दिखाई दे) तो आप लॉग फ़ाइल को दूषित कर सकते हैं।
  • यह दृष्टिकोण विशिष्ट है- tailयह अन्य कार्यक्रमों के साथ काम नहीं करेगा।
  • तीसरा पाइपलाइन चरण दूसरी पाइपलाइन चरण के रिटर्न कोड तक पहुंचना मुश्किल बनाता है (जब तक कि आप पॉसिक्स एक्सटेंशन जैसे कि bash'एस PIPESTATUSएरे ' का उपयोग नहीं कर रहे हों )। इस मामले में यह कोई बड़ी बात नहीं है क्योंकि grepहमेशा 0 वापस आ जाएगा, लेकिन सामान्य तौर पर मध्य चरण को एक अलग कमांड से बदला जा सकता है जिसका रिटर्न कोड आप परवाह करते हैं (जैसे, कुछ ऐसा जो 0 तब वापस आता है जब "सर्वर शुरू" का पता चलता है, 1 जब "सर्वर शुरू करने में विफल रहा है" का पता चला है)।

अगले दृष्टिकोण इन सीमाओं से बचते हैं।

एक बेहतर दृष्टिकोण: पाइपलाइनों से बचें

आप पाइपलाइन से बचने के लिए पूरी तरह से एक एफआईएफओ का उपयोग कर सकते हैं, जिससे निष्पादन एक बार grepरिटर्न जारी रखने की अनुमति देता है। उदाहरण के लिए:

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}"

टिप्पणी के साथ चिह्नित लाइनों को # optionalहटाया जा सकता है और कार्यक्रम अभी भी काम करेगा; tailजब तक यह इनपुट की एक और लाइन पढ़ता है या किसी अन्य प्रक्रिया से मारा जाता है, तब तक सिर्फ भटकता रहेगा।

इस दृष्टिकोण के फायदे हैं:

  • आपको लॉग फ़ाइल को संशोधित करने की आवश्यकता नहीं है
  • दृष्टिकोण इसके अलावा अन्य उपयोगिताओं के लिए काम करता है tail
  • यह एक दौड़ की स्थिति से ग्रस्त नहीं है
  • आप आसानी से grep(या जो भी वैकल्पिक आदेश आप उपयोग कर रहे हैं) का रिटर्न मान प्राप्त कर सकते हैं

इस दृष्टिकोण के लिए नकारात्मकता जटिलता है, विशेष रूप से फीफो का प्रबंधन: आपको सुरक्षित रूप से एक अस्थायी फ़ाइल नाम उत्पन्न करने की आवश्यकता होगी, और आपको यह सुनिश्चित करने की आवश्यकता होगी कि उपयोगकर्ता के बीच में Ctrl-C हिट होने पर भी अस्थायी FIFO हटा दिया जाए। लिपी। यह एक जाल का उपयोग करके किया जा सकता है।

वैकल्पिक दृष्टिकोण: मारने के लिए एक संदेश भेजें tail

आप tailइसे सिग्नल भेजकर बाहर निकलने के लिए पाइपलाइन चरण प्राप्त कर सकते हैं SIGTERM। चुनौती मज़बूती से कोड में एक ही जगह दो चीजों को जान रही है: tailपीआईडी ​​और क्या grepबाहर निकल गई है।

जैसे पाइपलाइन के साथ tail -f ... | grep ..., tailपृष्ठभूमि tailऔर पठन द्वारा एक चर में पीआईडी को बचाने के लिए पहले पाइपलाइन चरण को संशोधित करना आसान है $!। यह भी चलाने के लिए दूसरी पाइपलाइन चरण संशोधित करने के लिए आसान है killजब grepबाहर निकलता है। समस्या यह है कि पाइपलाइन के दो चरण अलग-अलग "निष्पादन वातावरण" (POSIX मानक की शब्दावली में) चलते हैं, इसलिए दूसरी पाइपलाइन चरण पहले पाइपलाइन चरण द्वारा निर्धारित किसी भी चर को नहीं पढ़ सकता है। शेल चर का उपयोग किए बिना, या तो दूसरे चरण में किसी भी तरह से tailपीआईडी ​​का पता लगाना चाहिए ताकि रिटर्न tailकरते समय वह मार सके grepया रिटर्न करते समय पहले चरण को किसी तरह अधिसूचित किया जाए grep

दूसरा चरण पीआईडी pgrepप्राप्त करने के लिए उपयोग कर सकता है tail, लेकिन यह अविश्वसनीय होगा (आप गलत प्रक्रिया से मेल खा सकते हैं) और गैर-पोर्टेबल ( pgrepपोसिक्स मानक द्वारा निर्दिष्ट नहीं है)।

पहला चरण पीआईडी ​​को पाइप के माध्यम से पीआईडी ​​को दूसरे चरण में भेज सकता है echo, लेकिन यह स्ट्रिंग tailआउटपुट के साथ मिश्रित हो जाएगी । इन दोनों के परिणाम के आधार पर, एक जटिल एस्केलिंग योजना की आवश्यकता हो सकती है tail

जब आप grepबाहर निकलते हैं तो पहले पाइपलाइन चरण को सूचित करने के लिए आप दूसरी पाइपलाइन चरण का उपयोग कर सकते हैं । फिर पहला चरण मार सकता है tail। यहाँ कुछ उदाहरण कोड है:

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}"

इस दृष्टिकोण में पिछले दृष्टिकोण के सभी पक्ष और विपक्ष हैं, सिवाय इसके कि यह अधिक जटिल है।

बफरिंग के बारे में चेतावनी

POSIX स्टड और स्टडआउट स्ट्रीम को पूरी तरह से बफ़र करने की अनुमति देता है, जिसका अर्थ है कि tailआउटपुट को grepमनमाने ढंग से लंबे समय तक संसाधित नहीं किया जा सकता है । जीएनयू सिस्टम पर कोई समस्या नहीं होनी चाहिए: जीएनयू grepउपयोग करता है read(), जो सभी बफरिंग से बचा जाता है, और जीएनयू स्टडआउट को लिखते समय tail -fनियमित कॉल करता है fflush()। गैर-जीएनयू सिस्टम को बफ़र्स को निष्क्रिय करने या नियमित रूप से फ्लश करने के लिए कुछ विशेष करना पड़ सकता है।


आप समाधान (दूसरों की तरह, मैं आपको दोष नहीं दूंगा) आपकी निगरानी शुरू होने से पहले ही लॉग फ़ाइल में लिखी गई चीजें याद आ जाएंगी। tail -fइच्छा ही उत्पादन पिछले दस लाइनों, और तब सभी निम्नलिखित। इसे सुधारने के लिए, आप -n 10000पूंछ में विकल्प जोड़ सकते हैं, इसलिए अंतिम 10000 लाइनें भी दी गई हैं।
अल्फ

एक और विचार: आपका फेनो सॉल्यूशन सीधा किया जा सकता है, मुझे लगता है, tail -fपेंडो के माध्यम से आउटपुट को पास करके और उस पर grepping mkfifo f; tail -f log > f & tailpid=$! ; grep -m 1 trigger f; kill $tailpid; rm f:।
एल्फ

@ शेल्फ: मैं गलत हो सकता हूं, लेकिन मेरा मानना ​​है कि tail -f logफीफो को लिखने से लाइन-आधारित बफरिंग के बजाय ब्लॉक-आधारित बफ़रिंग का उपयोग करने के लिए कुछ सिस्टम (जैसे, जीएनयू / लिनक्स) का कारण होगा, जिसका अर्थ है grepकि जब यह मिलान लाइन नहीं देख सकता है। लॉग में दिखाई देता है। सिस्टम बफ़रिंग को बदलने के लिए एक उपयोगिता प्रदान कर सकता है, जैसे कि stdbufGNU कोरुटिल्स से। हालांकि, ऐसी उपयोगिता गैर-पोर्टेबल होगी।
रिचर्ड हैनसेन

1
@ शेल्फ: वास्तव में, ऐसा लग रहा है कि POSIX एक टर्मिनल के साथ बातचीत करते समय बफरिंग के बारे में कुछ नहीं कहता है, इसलिए मानकों के परिप्रेक्ष्य से मुझे लगता है कि आपका सरल समाधान उतना ही अच्छा है जितना कि मेरा जटिल। हालाँकि, प्रत्येक मामले में विभिन्न कार्यान्वयन वास्तव में कैसे व्यवहार करते हैं, इसके बारे में मुझे 100% यकीन नहीं है।
रिचर्ड हैनसेन

वास्तव में, मैं अब grep -q -m 1 trigger <(tail -f log)कहीं और भी सरल प्रस्ताव के लिए जा रहा हूं और इस तथ्य के साथ जीना चाहता हूं कि tailपृष्ठभूमि में एक पंक्ति लंबे समय तक चलती है, जिसकी उसे जरूरत है।
अल्फ

9

मुझे @ 00prometheus उत्तर (जो सबसे अच्छा है) पर विस्तार करने दें।

हो सकता है कि आपको अनिश्चित काल तक प्रतीक्षा करने के बजाय टाइमआउट का उपयोग करना चाहिए।

नीचे दिए गए बैश फ़ंक्शन तब तक ब्लॉक होंगे जब तक कि दिए गए खोज शब्द प्रकट नहीं होते हैं या किसी दिए गए टाइमआउट तक नहीं पहुंचते हैं।

निकास स्थिति 0 होगी यदि स्ट्रिंग टाइमआउट के भीतर पाई जाती है।

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
}

शायद लॉग फ़ाइल अभी आपके सर्वर को लॉन्च करने के बाद मौजूद नहीं है। उस स्थिति में, आपको स्ट्रिंग की खोज करने से पहले उसके प्रदर्शित होने की प्रतीक्षा करनी चाहिए:

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))
}

यहां बताया गया है कि आप इसका उपयोग कैसे कर सकते हैं:

wait_server "/var/log/server.log" 5m && \
echo -e "\n-------------------------- Server READY --------------------------\n"

तो, timeoutआज्ञा कहाँ है ?
अयनांश

वास्तव में, का उपयोग timeoutकरना एकमात्र विश्वसनीय तरीका है जो अनिश्चित काल तक एक सर्वर के लिए इंतजार नहीं करता है जो शुरू नहीं हो सकता है और पहले से ही बाहर निकल चुका है।
ग्लुक 47

1
यह उत्तर सबसे अच्छा है। बस फ़ंक्शन को कॉपी करें और इसे कॉल करें, यह बहुत आसान है और पुन: प्रयोज्य है
Hristo Vrigazov

6

इसलिए कुछ परीक्षण करने के बाद, मुझे इस काम को करने के लिए एक त्वरित 1-लाइन तरीका मिला। ऐसा प्रतीत होता है कि grep क्विट होने पर टेल-ऑफ़ निकल जाएगा, लेकिन एक पकड़ है। यदि फ़ाइल को खोला और बंद किया जाता है तो यह केवल ट्रिगर होना प्रतीत होता है। जब grep मैच पाता है तो मैंने फ़ाइल को खाली स्ट्रिंग जोड़कर इसे पूरा किया है।

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

मुझे यकीन नहीं है कि फ़ाइल का खुला / बंद होना पूंछ को महसूस करता है कि पाइप बंद है, इसलिए मैं इस व्यवहार पर भरोसा नहीं करूंगा। लेकिन यह अभी के लिए काम करने लगता है।

कारण यह बंद हो जाता है, -F ध्वज, बनाम -f ध्वज को देखें।


1
यह काम करता है क्योंकि लॉगफाइल में शामिल होने tailसे दूसरी लाइन का उत्पादन होता है, लेकिन तब grepतक बाहर निकल गया है (शायद - वहां एक दौड़ की स्थिति है)। यदि grepसमय से बाहर निकल गया है tailएक और लाइन लिखता है, tailएक मिल जाएगा SIGPIPE। इससे tailतुरंत बाहर निकलने का कारण बनता है।
रिचर्ड हैनसेन

1
इस दृष्टिकोण के नुकसान: (1) एक दौड़ की स्थिति है (यह हमेशा तुरंत बाहर नहीं निकल सकता है) (2) यह लॉग फ़ाइल तक पहुंच लिखने की आवश्यकता है (3) आपको लॉग फ़ाइल को संशोधित करने के साथ ठीक होना चाहिए (4) आप भ्रष्ट हो सकते हैं लॉग फ़ाइल (5) यह केवल tail(6) के लिए काम करती है आप इसे अलग-अलग तरीके से अलग-अलग व्यवहार करने के लिए अलग-अलग स्ट्रिंग मैचों ("सर्वर प्रारंभ" बनाम "सर्वर प्रारंभ विफल") पर निर्भर नहीं कर सकते क्योंकि आप आसानी से रिटर्न कोड प्राप्त नहीं कर सकते पाइप लाइन के मध्य चरण में। एक वैकल्पिक दृष्टिकोण है जो इन सभी समस्याओं से बचा जाता है - मेरा उत्तर देखें।
रिचर्ड हैनसेन

6

वर्तमान में, जैसा कि दिया गया है, यहां सभी tail -fसमाधान पहले से लॉग की गई "सर्वर स्टार्टेड" लाइन (जो कि आपके विशिष्ट मामले में समस्या हो सकती है या नहीं हो सकती है, इस पर निर्भर करता है कि लॉग की गई लाइनों की संख्या और लॉग फाइल घुमाने पर निर्भर करता है) काट-छांट)।

अति-जटिल चीजों के बजाय, बस एक चालाक का उपयोग करें tail, जैसा कि bmike एक पर्ल स्निपिट के साथ दिखाया गया है। सबसे सरल समाधान यह है retailजिसमें स्टार्ट पैटर्न को रोकने और हालत पैटर्न के साथ regex समर्थन एकीकृत है :

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

यह फ़ाइल को एक सामान्य की तरह फॉलो करेगा, tail -fजब तक कि स्ट्रिंग का पहला नया उदाहरण दिखाई न दे, तब बाहर निकलें। ( -uविकल्प सामान्य "फ़ॉलो" मोड में होने पर फ़ाइल की अंतिम 10 लाइनों में मौजूदा लाइनों पर ट्रिगर नहीं होता है।)


यदि आप जीएनयू tail( कोर्यूटिल्स ) का उपयोग करते हैं , तो अगला सरलतम विकल्प का उपयोग करना है --pidऔर एक फीफो (नामित पाइप):

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

एक FIFO का उपयोग किया जाता है क्योंकि PID को प्राप्त करने और पास करने के लिए प्रक्रियाओं को अलग से शुरू किया जाना चाहिए। एक FIFO अभी भी SIGPIPEtail प्राप्त करने के लिए समय पर लिखने के लिए घूमने की एक ही समस्या से ग्रस्त है , विकल्प का उपयोग करें ताकि यह तब समाप्त हो जाए जब यह नोटिस समाप्त हो जाए (पारंपरिक रूप से पाठक के बजाय लेखक प्रक्रिया की निगरानी करने के लिए उपयोग किया जाता है , लेकिन doesn ' टी वास्तव में देखभाल)। विकल्प का उपयोग इसके साथ किया जाता है ताकि पुरानी रेखाएं एक मैच को ट्रिगर न करें।--pidtailgreptail-n 0tail


अंत में, आप एक स्टेटफुल टेल का उपयोग कर सकते हैं , यह वर्तमान फ़ाइल को स्टोर करेगा ताकि बाद के इनवोकेशन केवल नई लाइनें दिखा सकें (यह भी रोटेशन को नियंत्रित करता है)। यह उदाहरण पुराने FWTK retail* का उपयोग करता है :

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

* नोट, एक ही नाम, पिछले विकल्प के लिए अलग कार्यक्रम।

सीपीयू-हॉगिंग लूप के बजाय, राज्य फ़ाइल ( .${LOGFILE}.off) के साथ फ़ाइल के टाइमस्टैम्प की तुलना करें , और सो जाओ। -Tयदि आवश्यक हो, तो राज्य फ़ाइल का स्थान निर्दिष्ट करने के लिए " " का उपयोग करें , उपरोक्त वर्तमान निर्देशिका को मानता है। बेझिझक उस स्थिति को छोड़ दें, या लिनक्स पर आप inotifywaitइसके बजाय अधिक कुशल उपयोग कर सकते हैं :

retail "${LOGFILE:=server.log}" > /dev/null
while true; do
    inotifywait -qq "${LOGFILE}" && 
       retail "${LOGFILE}" | grep -q "Server Started" && break
done

क्या मैं retailटाइमआउट के साथ गठबंधन कर सकता हूं , जैसे: "अगर 120 सेकंड बीत चुके हैं और खुदरा अभी भी लाइन नहीं पढ़े हैं, तो एक त्रुटि कोड दें और रिटेल से बाहर निकलें"?
किलटेक

@kiltek timeoutलॉन्च करने के लिए GNU ( कोरुटिल्स) का उपयोग करें retailऔर टाइमआउट पर एक्जिट कोड 124 की जांच करें ( timeoutआपके द्वारा निर्धारित समय के बाद शुरू करने के लिए आप जो भी कमांड का उपयोग करेंगे उसे मार डालेंगे)
mr.spuratic

4

यह थोड़ा मुश्किल होगा क्योंकि आपको प्रक्रिया नियंत्रण और सिग्नलिंग में जाना होगा। पीआईडी ​​ट्रैकिंग का उपयोग करके अधिक कुल्डी एक दो स्क्रिप्ट समाधान होगा। बेहतर होगा कि इस तरह नामित पाइप का उपयोग किया जाए

आप किस शेल स्क्रिप्ट का उपयोग कर रहे हैं?

एक त्वरित और गंदे के लिए, एक स्क्रिप्ट समाधान - मैं फ़ाइल: टेल का उपयोग करके एक पर्ल स्क्रिप्ट बनाऊंगा

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

जबकि लूप के अंदर प्रिंट करने के बजाय, आप स्ट्रिंग स्क्रिप्ट के लिए फ़िल्टर कर सकते हैं और अपनी स्क्रिप्ट को जारी रखने के लिए लूप से बाहर निकल सकते हैं।

इनमें से किसी को भी आपके द्वारा मांगे जा रहे प्रवाह नियंत्रण को लागू करने के लिए बस थोड़ा सा सीखना शामिल करना चाहिए।


बैश का उपयोग करना। मेरा पर्ल-फू उतना मजबूत नहीं है, लेकिन मैं इसे एक शॉट दूंगा।
एलेक्स हॉफस्टीड

पाइप का उपयोग करें - वे बैश प्यार करते हैं और बैश उन्हें प्यार करते हैं। (जब आपका पाइप आपके किसी एक पाइप से
टकराएगा तो

maxinterval=>300इसका मतलब है कि यह हर पाँच मिनट में फ़ाइल की जाँच करेगा। चूँकि मुझे पता है कि मेरी लाइन फ़ाइल में क्षण भर में दिखाई देगी, इसलिए मैं बहुत अधिक आक्रामक मतदान का उपयोग कर रहा हूँ:maxinterval=>0.2, adjustafter=>10000
स्टीफन ओस्टरमिलर

2

फ़ाइल के आने की प्रतीक्षा करें

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

फ़ाइल में स्ट्रिंगर के लिए प्रतीक्षा करें

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

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


2
इस मतदान में दो मुख्य कमियां हैं: 1. यह बार-बार लॉग से गुजरकर गणना समय बर्बाद करता है। एक विचार करें /path/to/the.fileजो 1.4GB बड़ा है; तब यह स्पष्ट है कि यह एक समस्या है। 2. यह लॉग एंट्री दिखाई देने पर जरूरत से ज्यादा इंतजार करता है, सबसे खराब स्थिति में 10 एस।
अल्फ

2

मैं इस एक क्लीनर समाधान की कल्पना नहीं कर सकता:

#!/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

ठीक है, शायद नाम में सुधार हो सकता है ...

लाभ:

  • यह किसी विशेष उपयोगिताओं का उपयोग नहीं करता है
  • यह डिस्क पर नहीं लिखता है
  • यह इनायत से पूंछ को काटता है और पाइप को बंद कर देता है
  • यह बहुत छोटा और समझने में आसान है

2

ऐसा करने के लिए आपको पूंछ की आवश्यकता नहीं है। मुझे लगता है कि वॉच कमांड वही है जो आप देख रहे हैं। वॉच कमांड किसी फ़ाइल के आउटपुट पर नज़र रखता है और आउटपुट बदलने पर -g विकल्प के साथ समाप्त किया जा सकता है ।

watch -g grep -m 1 "Server Started" logfile.log && Yournextaction

1
क्योंकि यह हर दो सेकंड में एक बार चलता है, लॉग फ़ाइल में लाइन दिखाई देने के तुरंत बाद यह बाहर नहीं निकलता है। इसके अलावा, यह अच्छी तरह से काम नहीं करता है अगर लॉग फ़ाइल बहुत बड़ी है।
रिचर्ड हैनसेन


1

एलेक्स मुझे लगता है कि यह आपकी बहुत मदद करेगा।

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

यह कमांड logfile पर कभी एंट्री नहीं देगा लेकिन चुपचाप grep करेगा ...


1
यह काम नहीं करेगा - आपको इसके लिए आवेदन करना होगा logfileअन्यथा एक मनमाने ढंग से लंबे समय तक हो सकता है इससे पहले tailकि वह दूसरी पंक्ति और पता लगाता है कि grepमृत्यु हो गई है (के माध्यम से SIGPIPE)।
रिचर्ड हैनसेन

1

यहाँ एक बहुत बेहतर समाधान है जो आपको लॉगफाइल में लिखने की आवश्यकता नहीं है, जो कुछ मामलों में बहुत खतरनाक या असंभव है।

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

वर्तमान में इसका केवल एक साइड इफेक्ट है, tailजब तक अगली लाइन लॉग में नहीं लिखी जाती है तब तक यह प्रक्रिया पृष्ठभूमि में रहेगी।


tail -n +0 -fफ़ाइल की शुरुआत से शुरू होता है। tail -n 0 -fफ़ाइल के अंत से शुरू होता है।
स्टीफन ओस्टरमिलर

1
एक और साइड इफेक्ट मुझे मिलता है:myscript.sh: line 14: 7845 Terminated sh -c 'tail...
स्टीफन ओस्टरमिलर

मेरा मानना ​​है कि इस उत्तर में "अगली सूची" "अगली पंक्ति" होनी चाहिए।
स्टीफन ओस्टरमिलर

यह काम करता है, लेकिन tailपृष्ठभूमि में एक प्रक्रिया चलती रहती है।
cbaldan

1

यहाँ अन्य समाधानों में कई मुद्दे हैं:

  • यदि लॉगिंग प्रक्रिया पहले से ही डाउन है या लूप के दौरान नीचे जाती है तो वे अनिश्चित काल तक चलेंगे
  • एक लॉग का संपादन जो केवल देखा जाना चाहिए
  • अनावश्यक रूप से एक अतिरिक्त फ़ाइल लिखना
  • अतिरिक्त तर्क के लिए अनुमति नहीं

यहाँ मैं एक उदाहरण के रूप में टॉमकैट का उपयोग करके आया हूं (यदि आप इसकी शुरुआत करते समय लॉग देखना चाहते हैं तो हैश को हटा दें):

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"
}

1

tailआदेश backgrounded किया जा सकता है और इसके पीआईडी को प्रतिध्वनित grepsubshell। में grepsubshell बाहर निकलने पर एक जाल हैंडलर को मार सकता है tailआदेश।

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

1

उन सभी को पढ़ें। tldr: grep से पूंछ को समाप्त करना।

सबसे सुविधाजनक दो रूप हैं

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

और अगर आपके पास बैश है

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

लेकिन अगर वह पूंछ पृष्ठभूमि में बैठी है, तो आपको परेशान करती है, यहाँ एक पंद्रह या किसी अन्य उत्तर की तुलना में अच्छा तरीका है। बैश की आवश्यकता है।

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

या अगर यह पूंछ नहीं है जो चीजों का उत्पादन कर रहा है,

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

0

Inotify (inotifywait) का उपयोग करने का प्रयास करें

आप किसी भी फ़ाइल परिवर्तन के लिए inotifywait सेट करते हैं, फिर फ़ाइल को grep के साथ जांचें, यदि लूप से बाहर निकलने पर बस फिर से राईट न करें


इस तरह, पूरी फ़ाइल को हर बार कुछ लिखा जाने के बाद पुनः जाँचना होगा। लॉग फ़ाइलों के लिए अच्छी तरह से काम नहीं करता है।
ग्रिटिटी

1
एक और तरीका दो स्क्रिप्ट बनाने के लिए है: 1. पूंछ -f logfile.log | grep -m 1 "सर्वर प्रारंभ"> / tmp / पाया 2. firstscript.sh & MYPID = $!; inotifywait -e MODIFY / tmp / पाया; किल -किला - $ MYPID
ईवनगार्ड

मैं आपको पीआईडी ​​पर कब्जा दिखाने के लिए अपना जवाब संपादित करने और फिर इनोटिफ़ाइट का उपयोग करने के लिए प्यार करता हूं - एक सुरुचिपूर्ण समाधान जो किसी के लिए पकड़ना आसान होगा, लेकिन एक अधिक परिष्कृत उपकरण की आवश्यकता होगी।
bmike

क्या आप पर कब्जा करना चाहते हैं की एक पीआईडी? मैं इसे बनाने की कोशिश कर सकता हूं यदि आप थोड़ा और समझाते हैं कि आप क्या चाहते हैं
इवनगार्ड

0

जैसे ही लाइन लिखी जाती है आप छोड़ना चाहते हैं, लेकिन आप समय समाप्त होने के बाद भी छोड़ना चाहते हैं:

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

-2

इस बारे में कैसा है:

जबकि सच है; अगर [! -z $ (grep "myRegEx" myLog.log)]; फिर तोड़ो; फाई; किया हुआ

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.