पहली पंक्ति के बाद एक फ़ाइल का हिस्सा कैसे प्राप्त करें जो एक नियमित अभिव्यक्ति से मेल खाता है?


169

मेरे पास लगभग 1000 लाइनों वाली एक फाइल है। मुझे अपनी फ़ाइल का वह हिस्सा चाहिए, जो मेरे grep स्टेटमेंट से मेल खाता है।

अर्थात्:

$ cat file | grep 'TERMINATE'     # It is found on line 534

इसलिए, मैं आगे की प्रक्रिया के लिए लाइन 535 से लाइन 1000 तक की फाइल चाहता हूं।

मैं उसे कैसे कर सकता हूँ?


34
UUOC (बिल्ली का बेकार उपयोग):grep 'TERMINATE' file
याकूब

30
मुझे पता है कि, इसकी तरह मैं इसे इस तरह से उपयोग करता हूं। प्रश्न पर वापस आते हैं।
युगल जिंदल

3
यह पूरी तरह से ठीक प्रोग्रामिंग सवाल है, और स्टैकओवरफ्लो के लिए अच्छी तरह से अनुकूल है।
aioobe

13
@ जैकोब यह बिल्ली का बेकार उपयोग बिल्कुल भी नहीं है। इसका उपयोग किसी फ़ाइल को मानक आउटपुट में प्रिंट करना है, जिसका अर्थ है कि हम grepडेटा को पढ़ने के लिए मानक इनपुट इंटरफ़ेस का उपयोग कर सकते हैं , बजाय यह जानने के कि हम क्या पढ़ना चाहते हैं grep, और sed, और awk, और pandoc, और ffmpegआदि पर स्विच करें । एक फ़ाइल से। यह समय बचाता है क्योंकि हमें हर बार एक नया स्विच सीखने की ज़रूरत नहीं है जो हम एक ही काम करना चाहते हैं: एक फ़ाइल से पढ़ें।
6

@runeks मैं आपकी भावना से सहमत हूं - लेकिन आप इसे बिना बिल्ली के भी प्राप्त कर सकते हैं grep 'TERMINATE' < file:। शायद यह पढ़ने को थोड़ा कठिन बनाता है - लेकिन यह शेल स्क्रिप्टिंग है, इसलिए यह हमेशा एक समस्या है :)
LOAS

जवाबों:


307

निम्नलिखित TERMINATEफ़ाइल के अंत तक मेल खाते हुए लाइन को प्रिंट करेगा :

sed -n -e '/TERMINATE/,$p'

समझाया: उस पर अपनी स्क्रिप्ट निष्पादित करने के बाद प्रत्येक पंक्ति को प्रिंट करने के -nडिफ़ॉल्ट व्यवहार को अक्षम करता sedहै, -eएक स्क्रिप्ट को इंगित किया है sed, /TERMINATE/,$एक पता (लाइन) रेंज चयन है जिसका अर्थ TERMINATEहै कि फ़ाइल के अंत में नियमित अभिव्यक्ति (जैसे grep) से मेल खाता पहली पंक्ति ( $) , और pप्रिंट कमांड है जो वर्तमान लाइन को प्रिंट करता है।

यह उस लाइन से प्रिंट होगा TERMINATEजो फ़ाइल के अंत तक मेल खाने वाली लाइन का अनुसरण करती है :
(मिलान लाइन से EOF तक, मिलान लाइन सहित नहीं)

sed -e '1,/TERMINATE/d'

समझाया गया: 1,/TERMINATE/ एक पता (लाइन) श्रेणी चयन है जिसका अर्थ है कि पहली पंक्ति में इनपुट के लिए पहली पंक्ति TERMINATEनियमित अभिव्यक्ति से मेल खाती है , और dडिलीट कमांड है जो वर्तमान लाइन को हटा देती है और अगली लाइन पर छोड़ देती है। जैसा कि sedडिफ़ॉल्ट व्यवहार लाइनों को प्रिंट करना है, यह TERMINATE इनपुट के अंत के बाद लाइनों को प्रिंट करेगा ।

संपादित करें:

यदि आप पहले लाइनें चाहते हैं TERMINATE:

sed -e '/TERMINATE/,$d'

और यदि आप TERMINATEएक ही पास में 2 अलग-अलग फ़ाइलों में पहले और बाद में दोनों लाइनें चाहते हैं:

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

फ़ाइलों के पहले और बाद में समाप्ति के साथ पंक्ति होगी, इसलिए प्रत्येक को संसाधित करने के लिए आपको उपयोग करने की आवश्यकता है:

head -n -1 before
tail -n +2 after

EDIT2:

यदि आप सीड स्क्रिप्ट में फ़ाइलनाम को हार्ड-कोड नहीं करना चाहते हैं, तो आप कर सकते हैं:

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

लेकिन फिर आपको $अंतिम पंक्ति से अर्थ से बचना होगा ताकि शेल $wचर का विस्तार करने की कोशिश नहीं करेगा (ध्यान दें कि अब हम सिंगल कोट्स के बजाय स्क्रिप्ट के चारों ओर दोहरे उद्धरण चिह्नों का उपयोग करते हैं)।

मैं यह बताना भूल गया कि स्क्रिप्ट में फ़ाइल नाम के बाद नई लाइन महत्वपूर्ण है ताकि सेड को पता चले कि फाइलनेम समाप्त हो गए हैं।


संपादित करें: २०१६-०५३०

Sébastien Clément ने पूछा: "आप TERMINATEएक चर द्वारा हार्डकोड को कैसे प्रतिस्थापित करेंगे ?"

आप मिलान पाठ के लिए एक चर बनायेंगे और फिर इसे पिछले उदाहरण की तरह ही करेंगे:

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

पिछले उदाहरण के साथ मिलान पाठ के लिए एक चर का उपयोग करने के लिए:

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the 
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

इन मामलों में चर के साथ पाठ की जगह के बारे में महत्वपूर्ण बिंदु हैं:

  1. [ ] [ ]] $variablenameमें संलग्न चर ( ) का विस्तार नहीं होगा लेकिन [ ] के भीतर चर । इसलिए, आपको उन सभी को बदलना होगा यदि उनमें पाठ है जिसे आप एक चर के साथ बदलना चाहते हैं। single quotes'double quotes"single quotesdouble quotes
  2. sedपर्वतमाला भी एक होते हैं $और तुरंत की तरह एक पत्र के द्वारा पीछा कर रहे हैं: $p, $d, $w। उन्होंने यह भी चर की तरह दिखाई देगा विस्तार करने की है, तो आप उन से बचने के लिए है $एक बैकस्लैश [साथ पात्रों \] की तरह: \$p, \$d, \$w

हम TERMINATE से पहले लाइनों को कैसे प्राप्त कर सकते हैं और इस प्रकार से सभी को हटा सकते हैं?
युगल जिंदल

एक चर द्वारा हार्डकोडेड टर्मिनल को कैसे बदलेंगे?
सेबास्टियन क्लेमेंट

2
एक उपयोग का मामला जो यहां याद आ रहा है, वह है कि अंतिम मार्कर के बाद लाइनों को कैसे प्रिंट किया जाए (यदि फ़ाइल में उनमें से कई हो सकते हैं .. तो लॉग फ़ाइलों आदि के बारे में सोचें)।
Mato

पहली पंक्ति में sed -e "1,/$matchtext/d"होने पर उदाहरण काम नहीं करता है $matchtext। मुझे इसे बदलना पड़ा sed -e "0,/$matchtext/d"
१५:१५ बजे करलगा २ Kar ’

61

एक साधारण सन्निकटन के रूप में आप इसका उपयोग कर सकते हैं

grep -A100000 TERMINATE file

जिसके लिए greps TERMINATEऔर उस लाइन के बाद 100000 लाइनों तक आउटपुट होता है।

मैन पेज से

-A NUM, --after-context=NUM

मिलान लाइनों के बाद अनुगामी संदर्भ की NUM लाइनें प्रिंट करें। मैचों की सन्निहित समूहों के बीच एक समूह विभाजक (-) युक्त एक पंक्ति रखें। -O या --ऑनली-मिलान विकल्प के साथ, इसका कोई प्रभाव नहीं है और एक चेतावनी दी गई है।


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

3
मुझे लगता है कि यह एक व्यावहारिक समाधान है!
मिकेलगोट्टा

2
इसी तरह -B NUM, --before-reference = NUM ​​प्रिंटिंग लाइनों से पहले अग्रणी संदर्भ के NUM प्रिंट। मैचों की सन्निहित समूहों के बीच एक समूह विभाजक (-) युक्त एक पंक्ति रखें। -O या --ऑनली-मिलान विकल्प के साथ, इसका कोई प्रभाव नहीं है और एक चेतावनी दी गई है।
PiyusG

इस समाधान ने मेरे लिए काम किया क्योंकि मैं आसानी से जांचने के लिए मेरी स्ट्रिंग के रूप में चर का उपयोग कर सकता हूं।
जोस मार्टिनेज

3
अछा सुझाव! यदि आप संदर्भ के आकार के बारे में अनिश्चित हैं, तो आप fileइसके बजाय लाइनों की गणना कर सकते हैं :grep -A$(cat file | wc -l) TERMINATE file
लेमिंग

26

यहाँ उपयोग करने का एक उपकरण है awk:

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

यह कैसे काम करता है:

  1. हमने '' पाया '' वेरिएबल को शून्य पर सेट किया, गलत का मूल्यांकन किया
  2. अगर 'TERMINATE' का मिलान नियमित अभिव्यक्ति के साथ मिलता है, तो हम इसे एक पर सेट करते हैं।
  3. अगर हमारा 'पाया' वेरिएबल ट्रू का मूल्यांकन करता है, तो प्रिंट करें :)

यदि आप उन्हें बहुत बड़ी फ़ाइलों पर उपयोग करते हैं, तो अन्य समाधान बहुत अधिक मेमोरी का उपभोग कर सकते हैं।


सरल, सुरुचिपूर्ण और बहुत सामान्य। मेरे मामले में यह '###' की दूसरी घटना तक सब कुछ cat file | awk 'BEGIN{ found=0} /###/{found=found+1} {if (found<2) print }'
छाप रहा था

3
यहाँ उपयोग नहीं करने वाला एक उपकरण है catawkतर्क के रूप में एक या एक से अधिक फ़ाइल नाम लेने में पूरी तरह सक्षम है। यह भी देखें stackoverflow.com/questions/11710552/useless-use-of-cat
tripleee

9

अगर मैं अपने प्रश्न समझ में सही ढंग से आप लाइनों चाहते हैं के बाद TERMINATE , सहित नहीं TERMINATEलाइन। awkइसे सरल तरीके से कर सकते हैं:

awk '{if(found) print} /TERMINATE/{found=1}' your_file

स्पष्टीकरण:

  1. हालांकि सबसे अच्छा अभ्यास नहीं आप इस तथ्य पर भरोसा कर सकते हैं कि सभी संस्करण 0 या खाली स्ट्रिंग को परिभाषित नहीं करते हैं। तो पहली अभिव्यक्ति ( if(found) print) के साथ शुरू करने के लिए कुछ भी प्रिंट नहीं करेगा।
  2. प्रिंटिंग होने के बाद हम जांचते हैं कि क्या यह स्टार्टर-लाइन है (जिसे शामिल नहीं किया जाना चाहिए)।

यह सभी लाइनों प्रिंट होगा के बादTERMINATE लाइन।


सामान्यीकरण:

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

उदाहरण:

$ cat ex_file.txt 
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt 
A good line to include
And this line
Yep
$

स्पष्टीकरण:

  1. यदि अंत- रेखा पाई जाती है तो कोई मुद्रण नहीं किया जाना चाहिए। ध्यान दें कि यह चेक वास्तविक प्रिंटिंग से पहले किया जाता है ताकि परिणाम से अंतिम- पंक्ति को बाहर किया जा सके।
  2. यदि foundसेट है तो वर्तमान लाइन को प्रिंट करें ।
  3. यदि स्टार्ट- लाइन पाई जाती है, तो सेट करें found=1ताकि निम्नलिखित लाइनें प्रिंट हो जाएं। ध्यान दें कि यह चेक वास्तविक प्रिंटिंग के बाद किया जाता है ताकि परिणाम से स्टार्ट- लाइन को बाहर किया जा सके।

टिप्पणियाँ:

  • कोड इस तथ्य पर निर्भर करता है कि सभी awk-var डिफॉल्ट को 0 या खाली स्ट्रिंग को परिभाषित नहीं करते हैं। यह मान्य है, लेकिन सर्वोत्तम अभ्यास नहीं हो सकता है ताकि आप अ BEGIN{found=0}-अभिव्यक्ति की शुरुआत में जोड़ सकें ।
  • यदि कई स्टार्ट-एंड- स्टॉक्स पाए जाते हैं तो वे सभी मुद्रित होते हैं।

1
बहुत बढ़िया उदाहरण। सिर्फ़ 2 घंटे बिताए गए हैं csplit, sed, और जटिल awk कमांड के सभी तरीके। इतना ही नहीं मैंने जो चाहा, वह किया, लेकिन मुझे यह दिखाने के लिए काफी सरल दिखाया कि मुझे कुछ अन्य संबंधित चीजों को करने के लिए इसे कैसे संशोधित करना चाहिए। मुझे याद है कि जाग महान है और सिर्फ बकवास के अशोभनीय गड़बड़ में नहीं है। धन्यवाद।
user1169420

{if(found) print}जाग में एक विरोधी पैटर्न का एक सा है, यह ब्लॉक करने के लिए सिर्फ foundया found;अगर आप एक और फिल्टर की जरूरत के बाद से बदलने के लिए और अधिक मुहावरेदार है ।
user000001

@ user000001 कृपया समझाएं। मुझे समझ नहीं आ रहा है कि क्या बदला जाए और कैसे। वैसे भी मुझे लगता है कि इसके लिखे जाने का तरीका बहुत स्पष्ट है कि क्या हो रहा है।
उल्फ्रे

1
आप के awk '{if(found) print} /TERMINATE/{found=1}' your_fileसाथ बदल देंगे awk 'found; /TERMINATE/{found=1}' your_file, वे दोनों एक ही काम करना चाहिए।
user000001

7

निम्नलिखित की तरह बैश पैरामीटर विस्तार का उपयोग करें:

content=$(cat file)
echo "${content#*TERMINATE}"

क्या आप समझा सकते हैं कि आप क्या कर रहे हैं?
युगल जिंदल

मैंने "फ़ाइल" की सामग्री को $ सामग्री चर में कॉपी किया। फिर मैंने "TERMINATE" दिखाई देने तक सभी पात्रों को हटा दिया। यह लालची मिलान का उपयोग नहीं करता है, लेकिन आप $ {सामग्री ## * TERMINATE} द्वारा लालची मिलान का उपयोग कर सकते हैं।
म्यू किआओ

यहाँ बैश मैनुअल का लिंक दिया गया है: gnu.org/software/bash/manual/…
म्यू

6
यदि फ़ाइल 100GB आकार की है तो क्या होगा?
ज़नीक डे

1
डाउनवोट: यह भयानक है (फ़ाइल को एक चर में पढ़ना) और गलत (इसे उद्धृत किए बिना चर का उपयोग करना; और आपको ठीक से उपयोग करना चाहिए printfया सुनिश्चित करना चाहिए कि आप वास्तव में जानते हैं कि आप क्या कर रहे हैं echo।)।
ट्रिपल जूल 25'16

6

grep -A 10000000 IN TERMINATE ’फाइल

  • विशेष रूप से वास्तव में बड़ी फ़ाइल पर काम कर रहे सेड की तुलना में बहुत तेज है। यह 10M लाइन्स (या जो कुछ भी आप में डालते हैं) तक काम करता है ताकि आप जो कुछ भी हिट करें उसके बारे में संभालने के लिए इसे बड़ा बनाने में कोई नुकसान न हो।

4

इसके साथ sedया करने के कई तरीके हैं awk:

sed -n '/TERMINATE/,$p' file

इसके लिए लग रहा है TERMINATE आपकी फ़ाइल के और उस पंक्ति से फ़ाइल के अंत तक प्रिंट करता है।

awk '/TERMINATE/,0' file

यह ठीक वैसा ही व्यवहार है जैसा कि sed

यदि आप उस पंक्ति की संख्या जानते हैं जिससे आप मुद्रण शुरू करना चाहते हैं, तो आप इसे एक साथ निर्दिष्ट कर सकते हैं NR(रिकॉर्ड की संख्या, जो अंततः पंक्ति की संख्या को इंगित करता है):

awk 'NR>=535' file

उदाहरण

$ seq 10 > a        #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10

संख्या के लिए आपका उपयोग भी कर सकते हैंmore +7 file
123

इसमें मिलान रेखा शामिल है, जो इस प्रश्न में वांछित नहीं है।
१६:५६ पर

@ अच्छी तरह से, यह भी स्वीकृत उत्तर और 2 सबसे अधिक उत्थान के मामले में है, इसलिए समस्या भ्रामक शीर्षक के साथ हो सकती है।
फेडोरक्वी 'एसओ

3

यदि किसी कारण से, आप sed का उपयोग करने से बचना चाहते हैं, तो निम्नलिखित TERMINATEफ़ाइल के अंत तक मेल खाते हुए लाइन को प्रिंट करेगा :

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

और निम्नलिखित TERMINATEफ़ाइल के अंत तक निम्नलिखित पंक्ति मिलान से प्रिंट होगा :

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

एक प्रक्रिया में सेड क्या कर सकता है, यह करने के लिए 2 प्रक्रियाएँ होती हैं, और अगर grep और टेल के निष्पादन के बीच फ़ाइल बदलती है, तो परिणाम असंगत हो सकता है, इसलिए मैं sed का उपयोग करने की सलाह देता हूं। इसके अलावा, यदि फ़ाइल में नहीं है TERMINATE, तो 1 कमांड विफल रहता है।


फ़ाइल दो बार स्कैन की गई है। क्या होगा अगर यह 100GB आकार है?
ज़नीक

1
डाउनवोट किया गया क्योंकि यह एक भद्दा समाधान है, लेकिन फिर उत्थान किया गया क्योंकि 90% उत्तर कैविटीज है।
मैड फिजिसिस्ट


0

यह ऐसा करने का एक तरीका हो सकता है। यदि आप जानते हैं कि आपके पास आपके grep शब्द की कौन सी फ़ाइल है और आपकी फ़ाइल में कितनी लाइनें हैं:

grep -A466 'TERMINATE' फ़ाइल


1
यदि लाइन नंबर ज्ञात है, तो grepइसकी आवश्यकता भी नहीं है; आप बस उपयोग कर सकते हैं tail -n $NUM, तो यह वास्तव में एक जवाब नहीं है।
सैमवेन

-1

sed नौकरी के लिए एक बेहतर उपकरण है: sed -n '/ re /, $ p' फ़ाइल

कहाँ regexp है।

एक अन्य विकल्प है grep - aafter-reference flag। आपको अंत में एक नंबर पर पास होने की आवश्यकता है, फ़ाइल पर wc का उपयोग करना बंद करने के लिए सही मान देना चाहिए। इसे -n और अपनी मैच अभिव्यक्ति के साथ मिलाएं।


- बाद-संदर्भ ठीक है, लेकिन सभी मामलों में नहीं।
युगल जिंदल

क्या आप कुछ और सुझाव दे सकते हैं .. ??
युगल जिंदल

-2

ये फाइल के अंत तक अंतिम लाइन "TERMINATE" से सभी लाइनों को प्रिंट करेंगे:

LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG|tail -n 1|sed "s/:/ \\'/g"|awk -F" " '{print $1}'`
tail -n +$LINE_NUMBER $YOUR_FILE_NAME

एक पंक्ति संख्या निकालने के साथ grepताकि आप इसे खिला सकें tailएक बेकार एंटीपैटर्न है। फ़ाइल के अंत के माध्यम से मैच और प्रिंटिंग ढूंढना (या, इसके विपरीत, पहले मैच में मुद्रण और रोकना) सामान्य रूप से सामान्य, आवश्यक रेगेक्स टूल के साथ किया जाता है। बड़े पैमाने grep | tail | sed | awkपर भी है और अपने आप में grepऔर दोस्तों के एक बड़े पैमाने पर बेकार उपयोग है
15

मुझे लगता है कि वह * हमें कुछ ऐसा देने की कोशिश कर रहा था जो 'TERMINATE' के / पिछले उदाहरण को खोजेगा और उस उदाहरण से लाइनें देगा। अन्य कार्यान्वयन आपको पहले उदाहरण देते हैं। LINE_NUMBER को संभवतः इस तरह दिखना चाहिए: LINE_NUMBER = $ (grep -o -n 'TERMINATE' $ OSCAM_LOG | tail -n 1 | awk -F: '{प्रिंट $ 1}') शायद यह सबसे सुंदर तरीका नहीं है, लेकिन यह लगता है काम पूरा हो गया। ^। ^
fbicknel

... या एक पंक्ति में सभी, लेकिन बदसूरत: पूंछ -n + $ (grep -o -n 'TERMINATE' $ your_FILE_NAME | tail -n 1 | awk -F: '{प्रिंट $ 1}') $ your_FILE_NAME
fbicknel

.... और मैं $ your_FILE_NAME के ​​बदले में $ OSCAM_LOG को वापस लेने और संपादित करने जा रहा था ... लेकिन किसी कारण से नहीं कर सकता। कोई विचार नहीं है कि $ OSCAM_LOG कहाँ से आया है; मैंने बस मन ही मन इसे तोता। ऊ
फीकेल

अकेले Awk 101 में ऐसा करना एक सामान्य कार्य है। यदि आप पहले से ही लाइन नंबर प्राप्त करने के लिए पहले से अधिक सक्षम टूल का उपयोग कर रहे हैं, तो जाने दें tailऔर कार्य को अधिक सक्षम टूल में पूरी तरह से करें। वैसे भी, शीर्षक स्पष्ट रूप से "पहला मैच" कहता है।
ट्रिपल जूल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.