किसी अन्य फ़ाइल में पाई गई लाइनों के आधार पर किसी फ़ाइल से लाइनें निकालें


11

फ़ाइल file1.txt में लाइनें शामिल हैं जैसे:

/api/purchase/<hash>/index.html

उदाहरण के लिए:

/api/purchase/12ab09f46/index.html

फ़ाइल file2.csv में लाइनें शामिल हैं जैसे:

<hash>,timestamp,ip_address

उदाहरण के लिए:

12ab09f46,20150812235200,22.231.113.64 
a77b3ff22,20150812235959,194.66.82.11

मैं सभी लाइनों को हटाकर file2.csv को फ़िल्टर करना चाहता हूं जहां हैश का मान file1.txt में भी मौजूद है। यह कहने के लिए है:

cat file1.txt | extract <hash> | sed '/<hash>/d' file2.csv

या इस तरह का कुछ।

यह सीधा होना चाहिए, लेकिन मुझे लगता है कि यह काम करने में असमर्थ है।

क्या कोई इस कार्य के लिए एक कार्यशील पाइपलाइन प्रदान कर सकता है?

जवाबों:


13

cut -d / -f 4 file1.txt | paste -sd '|' | xargs -I{} grep -v -E {} file2.csv

स्पष्टीकरण:

cut -d / -f 4 file1.txt पहली फ़ाइल से हैश का चयन करेगा

paste -sd '|' एक नियमित अभिव्यक्ति पूर्व में सभी हैश में शामिल हो जाएगा। H1|H2|H3

xargs -I{} grep -v -E {} file2.csvएक तर्क के रूप में पिछले पैटर्न के साथ grep आह्वान करेगा, xargs {}की सामग्री के साथ बदल देगाSTDIN

यदि आपके पास नहीं है तो pasteआप इसे बदल सकते हैंtr "\\n" "|" | sed 's/|$//'


3
+1 लेकिन कोई ज़रूरत नहीं है cat, बस cut -d / -f 4 file1.txt। या यदि आप अनुक्रमिक रूप पसंद करते हैं,<file1.txt cut -d / -f 4
स्पार्कहॉक

@ श्रावक धन्यवाद! मुझे नहीं पता; ;-) समाधान अपडेट किया गया :-)
गैब्रियल

11

संभव awkसमाधान:

awk 'NR == FNR { x[$4] = 1; next; } { if (!($1 in x)) print $0; }' FS="/" file1.txt FS="," file2.txt

पहले हम (फ़ील्ड विभाजक) "/" file1.txtका उपयोग करते हुए पढ़ते हैं FSऔर फ़ील्ड से कुंजियों के मान के साथ सरणी x बनाते हैं $4जो वह हैश है जो आप चाहते हैं। आगे हम दूसरी फाइल file2.txtसेटिंग FSको पढ़ते हैं ,और जाँचते हैं कि $1क्या फ़ील्ड का मान सरणी में कुंजी के रूप में मौजूद नहीं है xऔर यदि यह प्रिंट नहीं करता है।
टिप्पणियों में प्रस्तावित के समान ही अधिक मुहावरेदार हो सकते हैं:

awk 'NR == FNR { x[$4] = 1; next; } !($1 in x)' FS="/" file1.txt FS="," file2.txt

मैं आपके प्रयास की सराहना करता हूं, लेकिन मुझे डर है कि यह मक्खी मेरे सिर के ऊपर है। मैं कुछ सेड / grep / बिल्ली के मिश्रण के आधार पर समाधान की उम्मीद करता हूं।
मार्को फॉस्टिनेली

1
मैं एक स्पष्टीकरण जोड़ूंगा, यह सरल है। और हो सकता है कि कोई व्यक्ति आपके इच्छित उपकरणों के साथ एक समाधान का प्रस्ताव देगा।
तालीज़िन

सिर्फ !($1 in x)इसके बजाय क्यों नहीं{ if (!($1 in x)) print $0; }
इरुवर

@ 1_CR यह मेरी बुरी आदत है, मुझे पता है कि यह अधिक मुहावरेदार हो सकता है लेकिन मुझे हमेशा लगता है कि यह ओपी के स्पष्टीकरण के लिए सरल होगा।
तालीज़िन

@Muzietto अभी भी, मुझे लगता है कि अन्य उपकरणों जैसे कि इस- awkआधारित समाधान को सीखने में कोई हानि नहीं है ... लंबे समय में, आप उन समाधानों की ओर बढ़ना सीखेंगे जिन्हें सादगी के लिए कम पाइपों का उपयोग करके प्राप्त किया जा सकता है ... :)
१k

5

के लिए जीएनयू sed

sed -z 's%.*/\([^/]*\)/index.html\n%\1\\|%g;s%^%/%;s%\\|$%/d%' file1.csv |
sed -f - file2.csv

जहां पहले सीड -कमांड-फॉर्मेट में हैश की सीड लिस्ट तैयार होती है /12ab09f46\|a77b3ff22\|..../dऔर इसे अगली सीड -स्क्रिप्ट में ट्रांसफर कर दिया जाता है जो कि इनपुट से कमांड के ऊपर पढ़ता है -f -
के साथ एक ही ग्रेप

grep -oP '[^/]*(?=/index.html$)' file1.csv | grep -Fvf - file2.csv

या perl-expresions के बिना:

grep -o '[^/]*/index.html$' file1.csv | 
grep -o '^[^/]*' | 
grep -Fvf - file2.csv

या कटौती के साथ भी बेहतर :

cut -d/ -f4 file1.csv | grep -Fvf - file2.csv

यह मुझे लगता है कि मैं क्या देख रहा था। क्या आप इसे थोड़ा स्पष्ट कर सकते हैं? मैं यह नहीं देख सकता कि दूसरा कमांड file2.csv से लाइनें कैसे हटाएगा।
मार्को फॉस्टिनेली

@Muzietto अपडेट देखें
कोस्टास

2
#!/bin/bash
cut -d, -f1 file2 | while read key ; do 
   #check for appearance in file1 with successful grep:
   #exit status is 0 if pattern is found, only search for at least 1
   #appearance -> to speed it up
   if [[ $(grep -m 1 "/$key/" file1) ]] ; then
      sed "/^$key,/d" -i file2
      #note that we are gradually overwriting file2 (-i option),
      #so make a backup!
   fi
done

ध्यान दें कि खोज स्टिंग कर रहे हैं /$key/और ^$key,परिणामों को कम करने के लिए या तो दो स्लैश (फ़ाइल 1) के बीच हो सकते हैं या एक पंक्ति की पहली प्रविष्टि और उसके बाद अल्पविराम (फ़ाइल 2) हो सकते हैं। यदि कुंजी दिखती है तो इसे सुरक्षित बनाना चाहिए

a,values
a1,values

फ़ाइल में 2, या पसंद है

/api/../a1/../
/api/../a/../

फ़ाइल 1 में


2

मैंने सिर्फ एक लाइनर की कोशिश की है, और यह काम करने के लिए लगता है:

 for i in `cat file1.txt  | awk -F"/" '{print $4}'`; do echo "\n $i" ; sed -ri "/^$i,/d" file2.csv ; done

कृपया पहले -ri को -re से बदलें यह परीक्षण करने के लिए। -एक सूखी चलाता है, और अगर सब ठीक है, तो आप इसे -ri के साथ चला सकते हैं


mmmh, मैंने आपके कोड के आउटपुट को एक अस्थायी फ़ाइल पर रीडायरेक्ट किया है और इसमें लगभग 30k लाइनें हैं, जबकि file2.csv में शुरू में 240 है और इसे फ़िल्टर किया जाना है।
मार्को फॉस्टिनेली

ठीक है, मुझे लगता है कि क्योंकि मैं पहली फ़ाइल में हर हैश को प्रिंट करता हूं, जब मैं प्रतिस्थापन (गूंज "\ n" $ i भाग) करता हूं। किसी भी तरह अगर आप इसे -ri के साथ चलाते हैं, तो आपको पुनर्निर्देशित नहीं करना है, क्योंकि यह जगह में प्रतिस्थापन करता है
प्रिमेरियो

इसके अलावा, यदि आप -re के साथ चलते हैं और आपके पास पहली फ़ाइल में जितने भी हैश हैं, उनके लिए आपने फ़ाइल 2 को दोहराया होगा। मूल रूप से पहली फ़ाइल में प्रत्येक हैश के लिए यह दूसरी फ़ाइल में बदल देता है और परिणाम को प्रिंट करता है, यही कारण है कि आपके पास कई लाइनें हैं।
प्रिमेरियो

1

के अतिरिक्त गैब्रिएल लाना के जवाब के कृपया ध्यान दें कि बीएसडी पेस्ट कमांड को मानक इनपुट से सामग्री को पढ़ने के लिए डैश की आवश्यकता होती है।

पेस्ट कमांड का मैनुअल

यदि '-' इनपुट फ़ाइलों में से एक या अधिक के लिए निर्दिष्ट है, तो मानक इनपुट का उपयोग किया जाता है; मानक इनपुट को एक बार में, '' - '' के प्रत्येक उदाहरण के लिए, एक पंक्ति में पढ़ा जाता है।

इसलिए अंतिम को नीचे की तरह बदलने की आवश्यकता है

cut -d / -f 4 file1.txt | paste -sd '|' - | xargs -I{} grep -v -E {} file2.csv
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.