एक फ़ाइल से लाइनें हटाना जो दूसरी फ़ाइल में हैं


126

मेरे पास एक फाइल है f1:

line1
line2
line3
line4
..
..

मैं उन सभी लाइनों को हटाना चाहता हूं जो किसी अन्य फ़ाइल में हैं f2:

line2
line8
..
..

मैंने कुछ के साथ कोशिश की catऔर sed, जो मेरे इरादे के करीब भी नहीं था। मैं यह कैसे कर सकता हूँ?



यदि आप ऐसी फ़ाइल से लाइनें निकालना चाहते हैं, जिसमें "सम्‍मिलित" दूसरी फ़ाइल से तार हैं (उदाहरण के लिए आंशिक मिलान) तो देखें unix.stackexchange.com/questions/145079/…
rogerdpack

जवाबों:


154

grep -v -x -f f2 f1 चाल चलनी चाहिए।

स्पष्टीकरण:

  • -v गैर-मिलान लाइनों का चयन करने के लिए
  • -x केवल पूरी रेखाओं का मिलान करने के लिए
  • -f f2 से पैटर्न प्राप्त करने के लिए f2

एक के बजाय का उपयोग कर सकते grep -Fया fgrepमैच के लिए तय तार से f2बजाय पैटर्न (मामले में आप नहीं बल्कि में लाइनों के उपचार की तुलना में एक "जो आप देखते है, तो क्या आप प्राप्त" तरीके से लाइनों को हटाने चाहते f2regex पैटर्न के रूप में)।


22
इसमें O (n has) जटिलता है और फ़ाइलों को कुछ K लाइनों से अधिक होने पर पूरा करने में घंटों लगेंगे।
अरनौद ले ब्लांक

11
यह पता लगाने कि एसओ ने सुझाव दिया कि अल्गोरिथम में ओ (एन ^ 2) जटिलता है केवल ओ (एन) जटिलता है, लेकिन अभी भी प्रतिस्पर्धा करने में घंटों लग सकते हैं।
एचडीव

2
मैं सिर्फ ~ 2k लाइनों की 2 फ़ाइलों पर यह कोशिश की, और यह ओएस द्वारा मार दिया गया (दी गई, यह एक बहुत शक्तिशाली वीएम नहीं है, लेकिन अभी भी)।
ट्रेबोर रूड

1
मुझे इस की लालित्य से प्यार है; मैं जोना क्रिस्टोफर साहनवाल के जवाब की गति को पसंद करता हूं।
एलेक्स हॉल

1
@ arnaud576875: क्या आप सुनिश्चित हैं? यह के कार्यान्वयन पर निर्भर करता है grep। यदि यह f2खोज शुरू करने से पहले ठीक से ठीक हो जाए तो खोज में केवल O (n) समय लगेगा।
हैलोगूडीबाई

57

इसके बजाय कॉम की कोशिश करें (f1 और f2 मानकर "पहले से ही हल किया हुआ है")

comm -2 -3 f1 f2

5
मुझे यकीन नहीं commहै कि समाधान है सवाल यह नहीं दर्शाता है कि लाइनें f1comm
छँटाई

1
यह मेरे लिए काम कर रहा था, क्योंकि मेरी फाइलें छंट गई थीं और उनमें से एक में २५०,०००+ लाइनें थीं, दूसरे में केवल २ in,०००। धन्यवाद!
शीतकालीन

1
जब यह काम करता है (इनपुट फ़ाइलों को सॉर्ट किया जाता है), यह बहुत तेज़ है!
माइक जार्विस

जैसा कि arnaud576875 के समाधान में, मेरे लिए cygwin का उपयोग करते हुए, इसने दूसरी फ़ाइल में डुप्लिकेट लाइनों को समाप्त कर दिया, जिसे रखना चाह सकते हैं।
एलेक्स हॉल

9
आप फ़ाइलों को पहले क्रमबद्ध करने के लिए प्रक्रिया प्रतिस्थापन का उपयोग कर सकते हैं, बेशक:comm -2 -3 <(sort f1) <(sort f2)
davemyron

14

उन फ़ाइलों को बाहर करने के लिए जो बहुत बड़ी नहीं हैं, आप AWK की सहयोगी सरणियों का उपयोग कर सकते हैं।

awk 'NR == FNR { list[tolower($0)]=1; next } { if (! list[tolower($0)]) print }' exclude-these.txt from-this.txt 

आउटपुट "से- it.txt" फ़ाइल के समान क्रम में होगा। tolower()अगर आपको लगता है कि जरूरत समारोह, यह केस-संवेदी बना देता है।

एल्गोरिथम की जटिलता शायद ओ (n) (अपवर्जित-इन टेक्स्ट आकार) + O (n) (from-this.txt आकार) होगी


आप ऐसी फाइलें क्यों कहते हैं जो बहुत बड़ी नहीं हैं? यहाँ डर है (मुझे लगता है) सिस्टम मेमोरी से सिस्टम को चलाने के लिए हैश बनाने के लिए जाग रहा है, या कुछ अन्य सीमा है?
रोजरपैक

अनुयायियों के लिए, लाइनों को "सैनिटाइज़" करने के लिए और भी अधिक आक्रामक विकल्प हैं (चूंकि साहचर्य सरणी का उपयोग करने के लिए तुलना करना सटीक है), पूर्व unix.stackexchange.com/a/145132/8337
rogerppack

@rogerdpack: एक बड़ी अपवर्जित फ़ाइल के लिए एक बड़े हैश सरणी (और एक लंबे प्रसंस्करण समय) की आवश्यकता होगी। एक बड़े "from-it.txt" को केवल एक लंबे प्रसंस्करण समय की आवश्यकता होगी।
अगली सूचना तक रोक दिया गया।

1
exclude-these.txtखाली होने पर यह विफल रहता है (अर्थात कोई आउटपुट नहीं देता है) । @ जोना-क्रिस्टोफर- sahnwaldt का उत्तर इस मामले में नीचे दिया गया है। आप कई फ़ाइलों को भी निर्दिष्ट कर सकते हैं जैसेawk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 done.out failed.out f=2 all-files.out
ग्राहम रसेल

11

डेनिस विलियमसन के उत्तर के समान (ज्यादातर वाक्यात्मक परिवर्तन, उदाहरण के लिए NR == FNRचाल के बजाय फ़ाइल संख्या स्पष्ट रूप से सेट करना ):

awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 exclude-these.txt f=2 from-this.txt

एक्सेस करने r[$0]से उस लाइन के लिए एंट्री हो जाती है, मान सेट करने की आवश्यकता नहीं होती।

यह मानकर कि awk निरंतर खोज और (औसतन) निरंतर अद्यतन समय के साथ एक हैश तालिका का उपयोग करता है, इस की समय जटिलता O (n + m) होगी, जहाँ n और m फाइलों की लंबाई हैं। मेरे मामले में, n ~ 25 मिलियन और m ~ 14000 था। जाग समाधान हल की तुलना में बहुत तेज था, और मैंने मूल आदेश को रखना भी पसंद किया।


यह डेनिस विलियमसन के उत्तर से कैसे भिन्न है? एकमात्र अंतर यह है कि यह हैश में एक असाइनमेंट नहीं करता है, इसलिए इससे थोड़ा तेज है? एल्गोरिथ्म जटिलता उसकी जैसी ही है?
रोजरपैक

अंतर ज्यादातर वाक्यात्मक है। मुझे वेरिएबल fक्लीयर लगता है NR == FNR, लेकिन यह स्वाद की बात है। हैश में असाइनमेंट इतना तेज होना चाहिए कि दो संस्करणों के बीच कोई औसत गति अंतर न हो। मुझे लगता है कि मैं जटिलता के बारे में गलत था - यदि लुकअप स्थिर है, तो अपडेट निरंतर (औसत रूप से) होना चाहिए। मुझे नहीं पता कि मुझे लगा कि अपडेट लॉगरिदमिक होगा। मैं अपना उत्तर संपादित करूँगा।
jcsahnwaldt

मैंने इन उत्तरों की एक गुच्छा की कोशिश की, और यह एक तेज था। मेरे पास सैकड़ों हजारों लाइनों वाली फाइलें थीं। एक जादू की तरह काम किया!
श्री टी। टी।

1
यह मेरा पसंदीदा उपाय है। यह कई फाइलों के साथ काम करता है और उदाहरण के लिए फाइलों को खाली भी करता है awk '{if (f==1) { r[$0] } else if (! ($0 in r)) { print $0 } } ' f=1 empty.file done.out failed.out f=2 all-files.out। जबकि अन्य awkसमाधान खाली बहिष्कृत फ़ाइल के साथ विफल रहता है और केवल एक ही ले सकता है।
ग्राहम रसेल

5

अगर आपके पास रूबी (1.9+) है

#!/usr/bin/env ruby 
b=File.read("file2").split
open("file1").each do |x|
  x.chomp!
  puts x if !b.include?(x)
end

जिसमें O (N ^ 2) जटिलता है। यदि आप प्रदर्शन के बारे में परवाह करना चाहते हैं, तो यहां एक और संस्करण है

b=File.read("file2").split
a=File.read("file1").split
(a-b).each {|x| puts x}

जो घटाव को प्रभावित करने के लिए हैश का उपयोग करता है, इसलिए जटिलता O (n) (a) का आकार + O (n) (b का आकार) है

यहाँ एक छोटा बेंचमार्क है, जो उपयोक्ता 576875 के सौजन्य से है, लेकिन उपरोक्त में से 100K लाइनों के साथ:

$ for i in $(seq 1 100000); do echo "$i"; done|sort --random-sort > file1
$ for i in $(seq 1 2 100000); do echo "$i"; done|sort --random-sort > file2
$ time ruby test.rb > ruby.test

real    0m0.639s
user    0m0.554s
sys     0m0.021s

$time sort file1 file2|uniq -u  > sort.test

real    0m2.311s
user    0m1.959s
sys     0m0.040s

$ diff <(sort -n ruby.test) <(sort -n sort.test)
$

diff यह दिखाने के लिए इस्तेमाल किया गया था कि उत्पन्न 2 फ़ाइलों के बीच कोई अंतर नहीं है।


1
इसमें O (n has) जटिलता है और फ़ाइलों को कुछ K लाइनों से अधिक होने पर पूरा करने में घंटों लगेंगे।
अरनौद ले ब्लांक

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

3
इतना रक्षात्मक होने की आवश्यकता नहीं है, यह ऐसा नहीं है जैसे कि @ user576875 ने आपके उत्तर या किसी भी चीज़ को अस्वीकार कर दिया हो। :-)
जॉन पार्कर

बहुत अच्छा दूसरा संस्करण, रूबी जीत :)
अरनौद ले ब्लांक

4

विभिन्न अन्य उत्तरों के बीच कुछ समय की तुलना:

$ for n in {1..10000}; do echo $RANDOM; done > f1
$ for n in {1..10000}; do echo $RANDOM; done > f2
$ time comm -23 <(sort f1) <(sort f2) > /dev/null

real    0m0.019s
user    0m0.023s
sys     0m0.012s
$ time ruby -e 'puts File.readlines("f1") - File.readlines("f2")' > /dev/null

real    0m0.026s
user    0m0.018s
sys     0m0.007s
$ time grep -xvf f2 f1 > /dev/null

real    0m43.197s
user    0m43.155s
sys     0m0.040s

sort f1 f2 | uniq -u सममितीय अंतर भी नहीं है, क्योंकि यह उन पंक्तियों को हटा देता है जो या तो फ़ाइल में कई बार दिखाई देती हैं।

स्टड के साथ कॉम का भी उपयोग किया जा सकता है और यहां तार:

echo $'a\nb' | comm -23 <(sort) <(sort <<< $'c\nb') # a

2

SQLite शेल के लिए उपयुक्त नौकरी लगती है:

create table file1(line text);
create index if1 on file1(line ASC);
create table file2(line text);
create index if2 on file2(line ASC);
-- comment: if you have | in your files then specify  .separator ××any_improbable_string×× 
.import 'file1.txt' file1
.import 'file2.txt' file2
.output result.txt
select * from file2 where line not in (select line from file1);
.q

1

क्या आपने इसे सेड के साथ आज़माया है?

sed 's#^#sed -i '"'"'s%#g' f2 > f2.sh

sed -i 's#$#%%g'"'"' f1#g' f2.sh

sed -i '1i#!/bin/bash' f2.sh

sh f2.sh

0

'प्रोग्रामिंग' का जवाब नहीं है, लेकिन यहाँ एक त्वरित और गंदा समाधान है: बस http://www.listdiff.com/compare-2-lists-difference-tool पर जाएं

जाहिर है बड़ी फ़ाइलों के लिए काम नहीं करेगा, लेकिन यह मेरे लिए चाल चली। कुछ नोट:

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