एकाधिक फ़ाइलों में स्ट्रिंग की अंतिम घटना का पता लगाएं


9

मुझे एक स्ट्रिंग की अंतिम घटना को खोजने के लिए कई लॉग फ़ाइलों (पिछले 24 घंटों में उत्पन्न सभी फ़ाइलों, सभी को एक ही निर्देशिका में) की खोज करने की आवश्यकता है। यह कमांड मैंने लिखा है:

find . -mtime 1 | grep fileprefix | xargs grep 'search string' | tail -1

लेकिन यह केवल एक फ़ाइल के लिए अंतिम पंक्ति है। कैसे सभी लाइनों को पाने के लिए इस पर कोई सुझाव?


क्या आपने पूंछ और आखिरी grep को पलटने की कोशिश की? खोजो। -टाइम 1 | grep fileprefix | xargs टेल -1 | grep 'खोज स्ट्रिंग'
Mathieu

जवाबों:


4

जीएनयू सुविधाओं को मानते हुए:

find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +

क्या आप 'bash -c \' के उद्देश्य को विस्तृत कर सकते हैं क्योंकि मैं पहले से ही bash शेल का उपयोग कर रहा हूँ। अंत में '_ {} +' का उद्देश्य भी।
लोकेश

@ लोकेश, आप findफ़ाइलों का उपयोग करके कमांड निष्पादित करने के लिए प्राप्त कर सकते हैं -exec। इसके साथ bash -c, हम एक bashशेल खोल रहे हैं जो प्रत्येक द्वारा पाई गई फ़ाइलों के माध्यम से लूप findकरता है और tac .. | grep -m1 fileprefixप्रत्येक पर कार्यान्वित होता है
iruvar

मैं कट कमांड सहित f के लिए लूप के लिए स्ट्रिंग फ़िल्टरिंग का विस्तार करने की कोशिश कर रहा था; tac "$ f" | ग्रेप -m1 fileprefix | कट -d '' -f4,7-8, लेकिन पल भर में मैंने कट कमांड डाल दी, इससे मुझे फाइल के अनपेक्षित अंत में त्रुटि हुई। क्या आप कृपया सुझाव दे सकते हैं कि मैं क्या गलत कर रहा हूँ।
लोकेश

@ लवकेश, -d" "कट के साथ उपयोग करें । सिंगल के बजाय डबल कोट्स
इरुवर

1
findआदेश फ़ाइल उपसर्ग के लिए फ़िल्टर कर सकते हैं; उसके grepलिए जरूरी नहीं होना चाहिए। यह भी आश्चर्य की बात है कि खोज स्ट्रिंग इस उत्तर में नहीं है।
जोनाथन लेफ्लर

8

यदि सब कुछ एक ही निर्देशिका में है, तो आप कर सकते हैं:

for file in *fileprefix*; do
    grep 'search string' "$file" | tail -1
done

यदि ये बड़ी फाइलें हैं, तो हो सकता है tacकि फाइल को रिवर्स ऑर्डर (अंतिम पंक्ति पहले) में प्रिंट करके और फिर grep -m1पहली घटना का मिलान करने के लिए चीजों को गति देने लायक हो । इस तरह, आप पूरी फ़ाइल पढ़ने से बचते हैं:

for file in *fileprefix*; do
    tac file | grep -m1 'search string'
done

उन दोनों को लगता है कि कोई निर्देशिका मेल नहीं खा रही है fileprefix। अगर वहाँ हैं, तो आपको एक त्रुटि मिलेगी जिसे आप अनदेखा कर सकते हैं। यदि यह समस्या है, तो केवल फ़ाइलों के लिए जाँच करें:

 for file in *fileprefix*; do
    [ -f "$file" ] && tac file | grep -m1 'search string'
 done

यदि आपको फ़ाइल नाम मुद्रित करने की आवश्यकता है, तो -Hप्रत्येक grepआह्वान में जोड़ें । या, यदि आपका grepसमर्थन नहीं करता है, तो इसे भी खोज के माध्यम से बताएं /dev/null। यह आउटपुट नहीं बदलेगा, लेकिन चूंकि grepकई फाइलें दी गई हैं, इसलिए यह हमेशा प्रत्येक हिट के लिए फाइल का नाम प्रिंट करेगा:

for file in *fileprefix*; do
    grep 'search string' "$file" /dev/null | tail -1
done

"इस तरह, आप पूरी फाइल पढ़ने से बचते हैं" - उह? नहीं, आप पूरी फाइल को grep में पढ़ने से बचते हैं लेकिन आप पूरी फाइल को tac के बजाय डालते हैं। मेरे लिए यह स्पष्ट नहीं है कि यह तेज़ होगा, हालांकि यह इस बात पर निर्भर करेगा कि मैच फ़ाइल की शुरुआत या अंत के पास था या नहीं।
गिल्स एसओ- बुराई को रोकना '

@ नहीं, आप tacया तो पूरी फाइल नहीं डालेंगे । पहला मैच मिलते ही यह बाहर निकल जाएगा। मैंने अभी-अभी 832M टेक्स्ट फाइल और आखिरी लाइन पर एक पैटर्न के साथ परीक्षण किया है। grep -m 1 pattern fileउपकरण ~ 7 सेकंड और tac file | grep -m1 patternलिया 0.009
terdon

4
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
     -exec sed -se'/searchstring/h;$!d;x' {} +

... अगर आपके पास जीएनयू sedहै जो -sएपरेट फाइल विकल्प और एक पॉसिक्स का समर्थन करता है तो काम करेगा find

आपको संभवतः ! -type dया -type fक्वालीफायर जोड़ना चाहिए , हालांकि, क्योंकि निर्देशिका को पढ़ने की कोशिश करना बहुत उपयोगी नहीं है, और आगे की सीमा को नियमित फ़ाइलों तक सीमित करने से पाइप या सीरियल डिवाइस फ़ाइल पर पढ़ने से बचा जा सकता है।

तर्क अविश्वसनीय रूप से सरल है - किसी भी इनपुट लाइन की एक प्रति के साथ sedअपनी hपुरानी जगह को ओवरराइट करता है जो मेल खाता है searchstring, फिर dआउटपुट से सभी इनपुट लाइनों को हटाता है लेकिन प्रत्येक इनपुट फ़ाइल के लिए अंतिम है। जब यह अंतिम लाइन पर पहुंच जाता है, तो यह xअपनी पकड़ और पैटर्न रिक्त स्थान को बदल देता है, और इसलिए यदि searchstringयह बिल्कुल भी पाया जाता है कि फाइल को पढ़ा जाए तो ऐसी अंतिम घटना आउटपुट के लिए स्वत: व्यवस्थित हो जाएगी, अन्यथा यह एक खाली लाइन लिखता है। ( यदि यह अवांछनीय है तो स्क्रिप्ट /./!dकी पूंछ में जोड़ें sed)

यह sedकुछ 65k इनपुट फ़ाइलों के प्रति एकल आह्वान करेगा - या आपकी ARG_MAXसीमा जो भी हो । यह एक बहुत अच्छा समाधान होना चाहिए, और काफी सरलता से लागू किया जाता है।

यदि आप चाहते हैं कि फिल्नामें भी दी जाएं, तो हाल ही में दिए गए GNU से sedआप उन्हें Fकमांड के साथ अलग-अलग पंक्तियों में लिख सकते हैं , या फिर आप उन्हें बाद findमें -printप्राइमरी में जोड़कर प्रति बैच में एक अलग सूची में प्रिंट करवा सकते हैं +


1

कैसा रहेगा:

find . -mtime -1 -name "fileprefix*" -exec sh -c \
'echo "$(grep 'search string' $1 | tail -n 1),$1"' _ {} \;

उपरोक्त आपको कॉमा के बाद संबंधित फ़ाइल नाम के बाद प्रत्येक फ़ाइल में खोज स्ट्रिंग की अंतिम घटना के साथ एक अच्छा आउटपुट देता है (स्वरूपण बदलने के लिए या अनावश्यक होने पर इसे हटाने के लिए प्रतिध्वनि के तहत ", $ 1" भाग। नमूना आउटपुट जो "फ़ाइल" नाम उपसर्ग के साथ फाइलों में '10' खोज स्ट्रिंग के लिए खोज निम्नानुसार है:

[dmitry@localhost sourceDir]$ find . -mtime -1 -name "file*" -exec  sh -c 'echo "$(grep '10' $1 | tail -n 1),$1"' _ {} \;
Another data 02 10,./file02.log
Some data 01 10,./file01.log
Yet another data 03 10,./file03.log 

1
find . -mtime 1 -name 'fileprefix*' -exec grep -Hn 'search string' {} + |
    sort -t: -k1,2 -n | 
    awk -F: '{key=$1 ; $1="" ; $2="" ; gsub(/^  /,"",$0); a[key]=$0} 
             END {for (key in a) { print key ":" a[key] }}'

इस का उपयोग करता है जीएनयू grepके -Hऔर -nविकल्प हमेशा दोनों फ़ाइल नाम और सभी मैचों की LineNumber मुद्रित करने के लिए, तो यह फ़ाइल नाम और LineNumber, और पाइप यह awk में, द्वारा सॉर्ट करता है जो भंडार एक सरणी में प्रत्येक फ़ाइल नाम के लिए आखिरी मैच है, और अंततः प्रिंट यह।

एक काफी जानवर-बल विधि, लेकिन यह काम करता है।

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