पहली पंक्ति को छोड़कर, फ़ाइल से अतिरिक्त हेडर लाइन निकालें


18

मेरे पास एक फ़ाइल है जो इस खिलौने के उदाहरण की तरह दिखती है। मेरी वास्तविक फ़ाइल में 4 मिलियन लाइनें हैं, जिनमें से लगभग 10 को मुझे हटाने की आवश्यकता है।

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
ID  Data1  Data2
4    100    100
ID  Data1  Data2
5    200    200

मैं हेडर की तरह दिखने वाली लाइनों को हटाना चाहता हूं, पहली लाइन को छोड़कर।

अंतिम फ़ाइल:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200

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

जवाबों:


26
header=$(head -n 1 input)
(printf "%s\n" "$header";
 grep -vFxe "$header" input
) > output
  1. हेडर लाइन को इनपुट फ़ाइल से वेरिएबल में पकड़ें
  2. हेडर प्रिंट करें
  3. grepहेडर से मेल खाने वाली लाइनों को हटाने के लिए फाइल को प्रोसेस करें
  4. आउटपुट फ़ाइल में उपरोक्त दो चरणों से आउटपुट कैप्चर करें

2
या शायद{ IFS= read -r head; printf '%s\n' "$head"; grep -vF "$head" ; } <file
इरुवर

दोनों अच्छे जोड़। धन्यवाद परोक्ष रूप से सिर से कि POSIX हाल ही में निकाले -1 वाक्य रचना उनका कहना है, -n 1. के पक्ष में करने के लिए don_crissti लिए
जेफ स्कालर

3
@JeffSchaller, हाल ही में 12 साल पहले की तरह। और head -1उससे पहले दशकों से इसका पालन किया जा रहा है।
स्टीफन चेज़लस

36

आप उपयोग कर सकते हैं

sed '2,${/ID/d;}'

यह 2 लाइन से शुरू होने वाली आईडी के साथ लाइनों को हटा देगा।


3
अच्छा; या पैटर्न मिलान के साथ और अधिक विशिष्ट, होना करने के लिए sed '2,${/^ID Data1 Data2$/d;}' file(, स्तंभों के बीच रिक्त स्थान की सही संख्या का उपयोग निश्चित रूप से)
जेफ स्कालर

हम्म ने सोचा कि आप केवल 1 कमांड के लिए अर्धविराम को छोड़ सकते हैं, लेकिन ठीक है।
1927 को bkmoney

W / sane seds नहीं, नहीं।
mikeserv

इन-द-एड संपादित जीत के लिए आआंद।
user2066657

4
याsed '1!{/ID/d;}'
स्टीफन चेज़लस

10

उन लोगों के लिए जो घुंघराले ब्रैकेट पसंद नहीं करते हैं

sed -e '1n' -e '/^ID/d'
  • nमतलब passलाइन नं।1
  • d सभी मिलान की गई पंक्ति (रेखाओं) को हटा दें, जो शुरू होती हैं ^ID

5
यह भी sed '1n;/^ID/d'फ़ाइल नाम के लिए छोटा किया जा सकता है । बस एक सुझाव
वैलेंटाइन बजरमी

ध्यान दें कि यह उन पंक्तियों को भी प्रिंट करेगा IDfooजो हेडर के समान नहीं हैं (इस मामले में अंतर करने की संभावना नहीं है, लेकिन आप कभी नहीं जानते हैं)।
terdon

6

यहाँ एक मजेदार है। आप sedपहली पंक्ति की सभी प्रतियों को छीनने के लिए सीधे उपयोग कर सकते हैं और बाकी सब जगह छोड़ सकते हैं (पहली पंक्ति सहित)।

sed '1{h;n;};G;/^\(.*\)\n\1$/d;s/\n.*$//' input

1{h;n;}पहली पंक्ति को होल्ड स्पेस में रखता है, इसे प्रिंट करता है, और अगली पंक्ति में पढ़ता है- sedपहली लाइन के लिए बाकी कमांड्स को छोड़ देता है। (यह दूसरी पंक्ति के लिए पहले 1परीक्षण को भी छोड़ देता है , लेकिन यह मायने नहीं रखता क्योंकि यह परीक्षा दूसरी पंक्ति पर लागू नहीं होगी)।

G होल्ड स्पेस की सामग्री द्वारा पैटर्न स्पेस के बाद एक नई पंक्ति को जोड़ता है।

/^\(.*\)\n\1$/dयदि नई पंक्ति के बाद का भाग (यानी जो होल्ड स्पेस से जोड़ा गया था) नई पंक्ति से पहले के भाग से बिल्कुल मेल खाता है, तो पैटर्न स्पेस की सामग्री को हटाता है (इस प्रकार अगली पंक्ति को छोड़ देता है)। यह वह जगह है जहां हेडर की नकल करने वाली लाइनें हटा दी जाएंगी।

s/\n.*$//पाठ का वह भाग हटा देता है जिसे Gकमांड द्वारा जोड़ा गया था , ताकि जो प्रिंट हो जाए वह फ़ाइल से पाठ की लाइन हो।

हालाँकि, चूंकि रेगेक्स महंगा है, इसलिए थोड़ी सी तेजी से एक ही स्थिति (नकारात्‍मक) का उपयोग किया जाएगा और Pयदि नई लाइन के बाद वाला भाग (यानी जो होल्ड स्‍पेस से जोड़ा गया है) के भाग पर मेल नहीं खाता है Newline से पहले और फिर बिना शर्त पैटर्न स्पेस हटाएं:

sed '1{h;n;};G;/^\(.*\)\n\1$/!P;d' input

अपना इनपुट दिए जाने पर आउटपुट:

ID  Data1  Data2
1    100    100
2    100    200
3    200    100
4    100    100
5    200    200


@don_crissti, दिलचस्प जोड़; धन्यवाद! मैं शायद लंबे समय तक लेकिन समकक्ष के लिए विकल्प चुनूंगा sed '1{h;n;};G;/^\(.*\)\n\1$/d;P;d' input; किसी तरह यह मेरे लिए पढ़ना आसान है। :)
वाइल्डकार्ड

इसके अलावा संबंधित: unix.stackexchange.com/a/417736/135943
वाइल्डकार्ड

5

यहाँ कुछ और विकल्प दिए गए हैं जिनकी आपको पहले से पहली पंक्ति जानने की आवश्यकता नहीं है:

perl -ne 'print unless $_ eq $k; $k=$_ if $.==1; 

-nझंडा, अपने इनपुट फ़ाइल पर पाश करने के लिए पर्ल बताता है के रूप में प्रत्येक पंक्ति की बचत $_। के रूप $k=$_ if $.==1;में पहली पंक्ति बचाता है ( $.पंक्ति संख्या है, इसलिए $.==1केवल 1 पंक्ति के लिए सही होगा) $kprint unless $k eq $_प्रिंट वर्तमान पंक्ति अगर यह एक ही एक में बचाया के रूप में नहीं है $k

वैकल्पिक रूप से, में एक ही बात awk:

awk '$0!=x;(NR==1){x=$0}' file 

यहां, हम परीक्षण करते हैं कि क्या वर्तमान पंक्ति वैसी ही है जैसा कि चर में सहेजी गई है x। यदि परीक्षण $0!=xसत्य का मूल्यांकन करता है (यदि वर्तमान रेखा $0समान नहीं है x), तो रेखा मुद्रित की जाएगी क्योंकि वास्तविक अभिव्यक्तियों पर जागरण के लिए डिफ़ॉल्ट क्रिया प्रिंट करना है। पहली पंक्ति ( NR==1) के रूप में सहेजी गई है x। चूंकि यह जांच करने के बाद किया जाता है कि क्या वर्तमान लाइन मेल खाती है x, यह सुनिश्चित करता है कि पहली पंक्ति भी मुद्रित होगी।


मुझे पहली पंक्ति के विचार को जानना पसंद नहीं है क्योंकि यह आपके टूलबॉक्स के लिए एक सामान्यीकृत स्क्रिप्ट बनाता है।
मार्क स्टीवर्ट

1
वह अजीब विधि प्रति पंक्ति एक खाली / झूठी सरणी प्रविष्टि बनाती है; 4M लाइनों के लिए अगर सभी अलग-अलग (क्यू से स्पष्ट नहीं) और काफी कम (ऐसा प्रतीत होता है) यह शायद ठीक है, लेकिन अगर बहुत अधिक या लंबी लाइनें हैं तो यह थ्रश या मर सकता है। !($0 in a)बनाने के बिना परीक्षण और इससे बचा जाता है, या awk वही तर्क कर सकता है जो आपके पास perl के लिए है: '$0!=x; NR==1{x=$0}'या यदि हेडर लाइन खाली हो सकती है'NR==1{x=$0;print} $0!=x'
dave_thompson_085

1
@ dave_thompson_085 प्रति पंक्ति एक सरणी कहाँ बनाई गई है? तुम्हारा मतलब है !a[$0]? वह क्यों एक प्रविष्टि बनाएगा a?
terdon

1
क्योंकि जागता है कि कैसे काम करता है; देख gnu.org/software/gawk/manual/html_node/... विशेष रूप से "नोट"।
dave_thompson_085

1
@ dave_thompson_085 अच्छी तरह से मैं शापित हो जाऊंगा! धन्यवाद, मुझे इसकी जानकारी नहीं थी। अभी तय किया है।
terdon

4

AWK इस तरह के उद्देश्य के लिए एक काफी सभ्य उपकरण है। यहाँ कोड का नमूना रन है:

$ awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt | head -n 10                                
ID  Data1  Data2
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100
1    100    100
     100    200
3    200    100

ब्रेक डाउन :

  • NR == 1 {print} हमें टेक्स्ट फाइल की पहली लाइन प्रिंट करने के लिए कहता है
  • NR != 1 && $0!~/ID Data1 Data2/ तार्किक ऑपरेटर &&AWK को उस लाइन को प्रिंट करने के लिए कहता है जो 1 के बराबर नहीं है और इसमें शामिल नहीं है ID Data1 Data2{print}भाग की कमी पर ध्यान दें ; जाग में अगर एक परीक्षण की स्थिति का सही मूल्यांकन किया जाता है, तो इसे मुद्रित करने के लिए लाइन के लिए मान लिया जाता है।
  • | head -n 10केवल 10 लाइनों के लिए उत्पादन को सीमित करने के लिए एक छोटा सा अतिरिक्त है। AWKभाग के लिए ही प्रासंगिक नहीं है, केवल डेमो उद्देश्य के लिए उपयोग किया जाता है।

यदि आप चाहते हैं कि किसी फ़ाइल में, कमांड > newFile.txtके अंत में कमांड के आउटपुट को रीडायरेक्ट करें , जैसे:

awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > newFile.txt

यह कैसे पकड़ है? वास्तव में बहुत अच्छा:

$ time awk 'NR == 1 {print} NR != 1 && $0!~/ID  Data1  Data2/' rmLines.txt > /dev/null                            
    0m3.60s real     0m3.53s user     0m0.06s system

पक्षीय लेख

उत्पन्न नमूना फ़ाइल को एक लाख से लूपिंग के लिए किया गया था और आपकी फ़ाइल की पहली चार पंक्तियों को प्रिंट किया गया था (इसलिए 4 लाइनें कई बार 4 लाख लाइनों के बराबर होती हैं), जो कि 0.09 सेकंड लेती थीं।

awk 'BEGIN{ for(i=1;i<=1000000;i++) printf("ID  Data1  Data2\n1    100    100\n     100    200\n3    200    100\n");  }' > rmLines.txt

ध्यान दें कि यह उन पंक्तियों को भी प्रिंट करेगा ID Data1 Data2 fooजो हेडर के समान नहीं हैं (इस मामले में अंतर करने की संभावना नहीं है, लेकिन आप कभी नहीं जानते हैं)।
terdon

@terdon हाँ, बिल्कुल सही। ओपी ने हालांकि केवल एक पैटर्न निर्दिष्ट किया जिसे वे निकालना चाहते हैं और उसका उदाहरण समर्थन करता प्रतीत होता है
सर्गियो कोलोडियाज़नी

3

Awk, किसी भी हेडर के लिए स्वचालित रूप से आदत डालना:

awk '( FNR == 1) {header=$0;print $0;}
     ( FNR > 1) && ($0 != header) { print $0;}'  file1  file2 ....

यानी, पहली पंक्ति पर, शीर्ष लेख प्राप्त करें और इसे प्रिंट करें, और बाद में उस हेडर से अलग रेखा प्रिंट करें।

FNR = वर्तमान फ़ाइल में रिकॉर्ड्स की संख्या, ताकि आपके पास कई फाइलें हो सकें और यह उनमें से प्रत्येक में ऐसा ही करेगा।


2

पूर्णता के लिए, पर्ल समाधान आईएमओ ने @terdon से थोड़ा अधिक सुरुचिपूर्ण दिया:

perl -i -p -e 's/^ID.*$//s if $. > 1' file

1
आह, लेकिन मेरा पूरा मुद्दा पैटर्न को निर्दिष्ट करने की आवश्यकता से बचने और इसके बजाय इसे पहली पंक्ति से पढ़ने के लिए था। आपका दृष्टिकोण बस शुरू होने वाली किसी भी लाइन को हटा देगा ID। आपके पास कोई गारंटी नहीं है कि यह उन पंक्तियों को नहीं हटाएगा जिन्हें रखा जाना चाहिए। चूंकि आप लालित्य लाए हैं, gअगर आप उपयोग करते हैं ^और $। वास्तव में, आपके सभी विकल्प m///यहां छोड़कर बेकार हैं s; वे उन सुविधाओं को सक्रिय करते हैं जिनका आप उपयोग नहीं कर रहे हैं। तो है $, s/^ID.*//sएक ही बात करना होगा।
terdon

@terdon, काफी साफ है। तुम्हारा बहुत अधिक सार्वभौमिक है!
KWubbufetowicz

2

बस थोड़ा सा सवाल पर पीछे धकेलने के लिए ... ऐसा लग रहा है कि शायद आपका इनपुट खुद कई TSV फ़ाइलों को एक साथ पूरा करने का परिणाम है। यदि आप अपनी प्रोसेसिंग पाइपलाइन में एक कदम उठा सकते हैं (यदि आप ऐसा करते हैं या ऐसा करने वाले लोगों से बात कर सकते हैं) तो आप पहली बार डेटा को सुरक्षित करने के लिए हेडर-अवेयर टूल का उपयोग कर सकते हैं, और इस तरह से होने की समस्या को दूर कर सकते हैं। अतिरिक्त हेडर लाइनों को हटा दें।

उदाहरण के लिए, मिलर का उपयोग करना :

$ cat f1.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
$ cat f2.tsv
ID  Data1 Data2
4 100 100
$ cat f3.tsv
ID  Data1 Data2
5 200 200

$ cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
ID  Data1 Data2
4 100 100
ID  Data1 Data2
5 200 200

$ mlr --tsvlite cat f1.tsv f2.tsv  f3.tsv
ID  Data1 Data2
1 100 100
2 100 200
3 200 100
4 100 100
5 200 200

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