विभिन्न फ़ाइलों के दो स्तंभों की तुलना करें और यदि यह मेल खाता है तो प्रिंट करें


16

मैं Solaris 10 का उपयोग कर रहा हूं और इसलिए grep के विकल्प शामिल हैं, जो काम नहीं करते हैं।

मेरे पास दो पाइप से अलग की गई फाइलें हैं:

file1:

abc|123|BNY|apple|
cab|234|cyx|orange|
def|kumar|pki|bird|

फ़ाइल 2:

abc|123|
kumar|pki|
cab|234

मैं file1 के पहले दो कॉलमों की file1 (पहले दो कॉलमों में file1 की संपूर्ण सामग्री के माध्यम से खोज) से तुलना करना चाहूंगा अगर वे file1 की मिलान रेखा से मेल खाते हैं। फिर फ़ाइल 2 और दूसरी की दूसरी पंक्ति के लिए खोजें।

अपेक्षित उत्पादन:

abc|123|BNY|apple|
cab|234|cyx|orange|

मेरे पास मौजूद फाइलें बड़ी हैं, जिनमें लगभग 400,000 लाइनें हैं, इसलिए मैं निष्पादन को तेज करना चाहता हूं।


मैंने आपके उदाहरणों से अग्रणी स्थान हटा दिए हैं, यदि आप इसे चाहते हैं, तो कृपया वापस संपादित करें। याद रखें कि रिक्त स्थान महत्वपूर्ण हैं, आपको केवल उनके पास होना चाहिए यदि वे आपकी वास्तविक फाइलों में मौजूद हैं।
terdon

के GNU संस्करण का उपयोग करने का प्रयास करें grep, यह निम्न है /usr/sfw/bin/ggrepstackoverflow.com/questions/15259882/...
SLM

जवाबों:


21

यह वही है जो awk के लिए डिज़ाइन किया गया था:

$ awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0' file2 file1
abc|123|BNY|apple|
cab|234|cyx|orange|

व्याख्या

  • -F'|': के लिए क्षेत्र विभाजक सेट करता है |
  • NR==FNR: एनआर वर्तमान इनपुट लाइन नंबर और एफएनआर वर्तमान फाइल की लाइन नंबर है। दोनों केवल 1 फ़ाइल पढ़ने के दौरान समान होंगे।
  • c[$1$2]++; next: यदि यह पहली फ़ाइल है, तो cसरणी में पहले दो फ़ील्ड सहेजें । फिर, अगली पंक्ति पर जाएं ताकि यह केवल 1 फ़ाइल पर लागू हो।

  • c[$1$2]>0: दूसरे ब्लॉक को केवल तभी निष्पादित किया जाएगा यदि यह दूसरी फ़ाइल है इसलिए हम जांचते हैं कि क्या इस फ़ाइल के फ़ील्ड 1 और 2 पहले ही देखे जा चुके हैं ( c[$1$2]>0) और यदि वे गए हैं, तो हम लाइन प्रिंट करते हैं। में awk, डिफ़ॉल्ट क्रिया लाइन को प्रिंट करने के लिए है इसलिए यदि c[$1$2]>0यह सत्य है, तो लाइन प्रिंट की जाएगी।


वैकल्पिक रूप से, जब से आपने पर्ल के साथ टैग किया है:

perl -e 'open(A, "file2"); while(<A>){/.+?\|[^|]+/ && $k{$&}++};
         while(<>){/.+?\|[^|]+/ && do{print if defined($k{$&})}}' file1

व्याख्या

पहली पंक्ति खुल जाएगी file2, 2 डी तक सब कुछ पढ़ें |( .+?\|[^|]+) और हैश $&में ( अंतिम मैच ऑपरेटर का परिणाम है ) सहेजें %k

दूसरी पंक्ति फ़ाइल 1 को संसाधित करती है, पहली दो कॉलम को निकालने के लिए उसी रेगेक्स का उपयोग करती है और यदि उन कॉलम को %kहैश में परिभाषित किया जाता है तो लाइन को प्रिंट करें ।


उपरोक्त दोनों दृष्टिकोणों को फ़ाइल 2 के पहले कॉलम को मेमोरी में रखने की आवश्यकता होगी। यह समस्या नहीं होनी चाहिए यदि आपके पास केवल कुछ सौ हजार लाइनें हैं, लेकिन यदि यह है, तो आप कुछ ऐसा कर सकते हैं

cut -d'|' -f 1,2 file2 | while read pat; do grep "^$pat" file1; done

लेकिन वह धीमा होगा।


लेकिन क्या यह सभी (पहले दो कॉलम) file2मेमोरी में लोड नहीं होगा ?
जोसेफ आर।

@terdon: awk -F'|' 'NR==FNR{c[$1$2]++;next};c[$1$2] > 0'छोटा संस्करण है।
congonglm

यह काम नहीं करता है ..
user68365

@ user68365: क्या file2डुप्लीकेट पंक्तियाँ हैं?
congonglm

नहीं, इसकी कोई डुप्लिकेट पंक्तियाँ नहीं हैं
user68365

1

मुझे लगता है

grep -Ff file2 file1

वह है जो आप ढूंढ रहे हैं। यह कुशल होना चाहिए, लेकिन मुझे यकीन नहीं है कि यह उतना सटीक होगा जितना आप चाहते हैं। यदि abc|123(उदाहरण के लिए) file1अलग-अलग कॉलम में एक लाइन में पाया जाता है , तो उस लाइन को भी प्रिंट किया जाएगा। यदि आप गारंटी दे सकते हैं कि ऐसा कभी नहीं होगा, तो उपरोक्त लाइन काम करना चाहिए।


ग्रेप पर्याप्त नहीं होगा, क्योंकि abc | 123 वें फ़ाइल में कहीं मौजूद हो सकता है। इसके अलावा मैं सोलारिस 10 का उपयोग कर रहा हूं और मैं उस grep विकल्प का भी उपयोग करने में असमर्थ हूं।
user68365

2
@ user68365 कृपया अपने प्रश्न में यह सब स्पष्ट करें। आपको हमें अपना ओएस बताना होगा और निर्दिष्ट करना होगा कि आप केवल पहले 2 कॉलम का मिलान करना चाहते हैं।
terdon

1

यदि आप एसक्यूएल की तरह समस्या पर विचार करना चाहते हैं, तो आपको निश्चित रूप से ' क्यू ' नामक एक उपकरण का प्रयास करना चाहिए।

$ q -d '|' "select f1.* from file1 f1 join file2 f2 on (f1.c1 = f2.c1 and f1.c2 = f2.c2)"

यदि आप SQL क्वेरी से परिचित हैं तो यह अधिक स्पष्ट और आसान है।


कम से कम क्रिप्टिक समाधानों में से एक के लिए धन्यवाद। मैं यही चाहता था। लेकिन मुझे इस "q टूल" को
रॉल्फ

बहुत उपयोगी उपकरण।
ghilesZ

0
$  sed 's/^/\^/' 2.txt > temp.txt ; grep 1.txt -f temp.txt
abc|123|BNY|apple|
cab|234|cyx|orange|

1
जैसा कि मैंने संपादित किया है और प्रश्न में उल्लेख किया है, मेरे सिस्टम में grep -f विकल्प काम नहीं करता है
user68365

सोलारिस 10 में एक ग्नू कोर-बर्तन / usr / sfw / bin Use / usr / sfw / bin / sed और / usr / sfw / bin / grep है
mr_tron
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.