लाइनों के क्रम को बनाए रखते हुए डुप्लिकेट लाइनें निकालें


14
[root@server]# awk '!seen[$0]++' out.txt > cleaned
awk: (FILENAME=out.txt FNR=8547098) fatal error: internal error
Aborted
[root@server]#

"" सर्वर "" में है: 8 GByte RAM + 16 GByte SWAP, x> 300 GByte मुक्त स्थान, amd64, डेस्कटॉप CPU। वैज्ञानिक लिनक्स 6.6। LOAD बनाने के लिए इस पर और कुछ नहीं चलता है। कुछ सेकंड के बाद अवाक गर्भपात हो जाता है .. out.txt ~ 1.6 GByte है। GNU Awk 3.1.7।

प्रश्न : मैं लाइनों के क्रम को बनाए रखते हुए डुप्लिकेट लाइनों को कैसे हटा सकता हूं? मामला भी महत्वपूर्ण है, पूर्व: "ए" और "ए" दो अलग लाइन है, इसे रखना होगा। लेकिन "ए" और "ए" डुप्लिकेट है, केवल पहले एक की आवश्यकता है।

उत्तर किसी भी चीज़ में हो सकता है .. अगर जागना इसके लिए अच्छा नहीं है .. तो perl / sed .. समस्या क्या हो सकती है?

[root@server]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 61945
max locked memory       (kbytes, -l) 99999999
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 99999999
cpu time               (seconds, -t) unlimited
max user processes              (-u) 61945
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@server]# 

अद्यतन: मैंने एक आरएचईएल मशीन पर यह कोशिश की, यह गर्भपात नहीं करता है, लेकिन मेरे पास इसे खत्म करने के लिए इंतजार करने का समय नहीं था .. क्यों एसएल लिनक्स आरएचईएल से अलग नहीं है?

अद्यतन: मैं एक Ubuntu 14 आभासी gues पर कोशिश कर रहा हूँ .. अब तक यह काम करता है! यह एक ulimit समस्या नहीं है: mawk 1.3.3

root@asdf-VirtualBox:~# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 51331
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 51331
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
root@asdf-VirtualBox:~# 

2
आपके उदाहरण में कोई डुप्लिकेट लाइनें नहीं हैं ...?
mikeserv

1
awkदो मशीनों में संस्करण क्या हैं ?
cuonglm

अप-टू-डेट rhel और अप-टू-डेट sl linux, rhel version को नहीं जानते .. sl है: GNU Awk 3.1.7
somelooser28533

कितना बड़ा है out.txt? यदि आप इसे एक छोटी फ़ाइल पर आज़माते हैं तो क्या यह एक ही कमांड काम करता है? मशीन पर कितने उपयोगकर्ता? क्या प्रक्रिया के लिए पर्याप्त उपलब्ध स्मृति थी? क्या इनपुट फ़ाइल की लाइन 8547098 के बारे में कुछ खास है?
terdon

जवाबों:


22

मुझे संदेह है कि इससे फर्क पड़ेगा, लेकिन सिर्फ मामले में, पर्ल में यही काम कैसे करना है:

perl -ne 'print if ++$k{$_}==1' out.txt

यदि समस्या अद्वितीय पंक्तियों को स्मृति में रख रही है, तो आपके पास वैसा ही मुद्दा होगा जैसा awkआपने प्रयास किया था। तो, एक और दृष्टिकोण हो सकता है:

cat -n out.txt | sort -k2 -k1n  | uniq -f1 | sort -nk1,1 | cut -f2-

यह काम किस प्रकार करता है:

  1. एक जीएनयू प्रणाली पर, cat -nकुछ स्थानों पर और बाद में एक <टैब> चरित्र के बाद प्रत्येक पंक्ति में लाइन नंबर को प्रीपेंड किया जाएगा । catइस इनपुट का प्रतिनिधित्व करता है sort

  2. sortके -k2विकल्प यह केवल लाइन के अंत तक दूसरे क्षेत्र से पात्रों पर विचार करने के लिए जब छँटाई का निर्देश है, और sortसफेद-अंतरिक्ष पर विभाजन क्षेत्र डिफ़ॉल्ट रूप से (या cat'डाला रिक्त स्थान और एस <टैब> )
    जब पीछा किया जाता है -k1n, sortतो पहले 2 क्षेत्र पर विचार करता है, और फिर समान -k2क्षेत्रों के मामले में दूसरा- यह 1 क्षेत्र पर विचार करता है, लेकिन संख्यात्मक रूप से हल किया जाता है। इसलिए बार-बार लाइनों को एक साथ क्रमबद्ध किया जाएगा लेकिन वे दिखाई देने वाले क्रम में।

  3. परिणामों को पाइप किया जाता है uniq- जिसे पहले क्षेत्र को अनदेखा करने के लिए कहा जाता है ( -f1और व्हॉट्सएप द्वारा अलग भी किया जाता है) - और जिसके परिणामस्वरूप मूल फ़ाइल में अद्वितीय लाइनों की सूची होती है और इसे वापस पाइप किया जाता है sort
  4. इस बार sortपहले क्षेत्र ( cat'सम्मिलित पंक्ति संख्या) पर क्रमबद्ध रूप से, मूल फ़ाइल में यह क्या था के लिए क्रम क्रम वापस मिल रहा है और इन परिणामों को पाइप करता है cut
  5. अंत में, cutउस लाइन नंबर को हटा देता है जिसे द्वारा डाला गया था cat। यह cutलाइन के अंत के माध्यम से केवल 2 के क्षेत्र से मुद्रण द्वारा प्रभावित होता है (और cutडिफॉल्ट सीमांकक एक <टैब> वर्ण है)

उदाहरण देकर स्पष्ट करने के लिए:

$ cat file
bb
aa
bb
dd
cc
dd
aa
bb
cc
$ cat -n file | sort -k2 | uniq -f1 | sort -k1 | cut -f2-
bb
aa    
dd
cc

हाय टेर्डन, ओपी को लाइनों के क्रम को रखने की आवश्यकता है, इसलिए बिल्ली | सॉर्ट | यूनीक विधि काम नहीं करेगी ... अपने पर्ल संस्करण की तरह ...
लैंबर्ट

1
के साथ अच्छा समाधान sort! लेकिन सबसे sortकर सकते हैं uniqअपने आप में तो आप कम कर सकते हैं आप स्क्रिप्ट के द्वारा sort -uk2 | sort -bk1,1n
कोस्टास

@ कोस्टस यह सबसे अधिक है sort? मुझे लगा -uकि एक GNU फीचर है।
terdon

@don_crissti आह, तो यह है, धन्यवाद। हालांकि मैं इसे यहाँ कैसे उपयोग कर सकता हूँ? जैसा कि मैंने अभी देखा (और ठीक करने के लिए संपादित किया गया है), मुझे पहले 2 फ़ील्ड पर क्रमबद्ध करने की आवश्यकता है और फिर 1 पर संख्यात्मक रूप से लाइन ऑर्डर रखने के लिए। मैं तब कैसे उपयोग कर सकता हूं -uऔर यह निर्दिष्ट कर सकता हूं कि इसे 1 फ़ील्ड को अनदेखा करना चाहिए? के अनुसार man sort, यह -uसंभव विकल्पों में से एक नहीं है -f, इसलिए मुझे नहीं लगता कि इसका उपयोग यहां किया जा सकता है।
terdon

1
यह है श्वार्टज़ियन परिवर्तन है ! (+1)
जजॉओ

7
#!/usr/bin/perl 
use DB_File;
tie %h, 'DB_File';

while(<>){ not $h{$_} and print and $h{$_}=1 }

संपादित 1: यह वास्तव में काम करता है? (की तुलना)

Sol1 : Terdon et all Schwartzian-transform-like one-liner
    cat -n _1 | sort -uk2 | sort -nk1 | cut -f2-

Sol2 : perl  + DB_File (this answer)
    perl dbfile-uniq _1

Sol3 : PO (John W. Gill solution has a similar behavior)
    awk '!seen[$0]++' _1

Sol4: Terdon perl
    perl -ne 'print if ++$k{$_}==1' _1

Case1 : 100_000_000 यादृच्छिक संख्या (प्रत्येक 5 अंक), 566Mbytes, 31_212 विभिन्न मूल्य:

$ while true ; do echo $RANDOM; done | head -100000000 > _1

केस 2 : 50_000_000 रैंड नंबर (10 अंक प्रत्येक), 516Mbytes, 48_351_464 1 मान:

$ shuf _1 |  sed 'N;s/\n/ /' > _11

(निम्नलिखित संख्या बहुत सटीक नहीं हैं):

┌────────┬────────┬────────────────┬────────┬──────┐
         Sol1    Sol2            Sol3    Sol4 
         sort...│ perl DB         awk     perl 
├────────┼────────┼────────────────┼────────┼──────┤
 case 1  6m15    6m17            0m28    0m28 
├────────┼────────┼────────────────┼────────┴──────┤
 case 2  11m15   81m44           out of memory 
├────────┼────────┼────────────────┼────────┬──────┤
 case 2          5m54 /cache=2G               
└────────┴────────┴────────────────┴────────┴──────┘

कैश के साथ sol2 है:

use DB_File;
use Fcntl ;

$DB_HASH->{'cachesize'} = 2000_000_000;
tie %h, 'DB_File', "_my.db", O_RDWR|O_CREAT|O_TRUNC, 0640, $DB_HASH;

while(<>){ not $h{$_} and print and $h{$_}=1 }

सॉर्ट भी एक कैश विकल्प (नहीं किया गया) को जोड़ने का अनुकूलन किया जा सकता है।

एक त्वरित निष्कर्ष:

  • sort एक शानदार आदेश है!

1
sort -uk2और sort -nk1,1अलग हैं। पहला 2cd कुंजी से पंक्ति के अंत तक विचार करता है, दूसरा केवल पहली कुंजी मानता है । आपको अपना परिवर्तन करना चाहिए sort -nk1- यह उस तरह से तेज़ भी हो सकता है, लेकिन यह निश्चित रूप से अधिक विश्वसनीय होगा। वैसे - वे कुछ सुंदर बक्से हैं।
mikeserv

@ mikeserv, टिप्पणी के लिए धन्यवाद। जैसा कि K1,1 अद्वितीय है, सॉर्ट -nk1 और सॉर्ट -nk1,1 कुछ परिणाम लौटाते हैं। मैंने दोनों की कोशिश की, परिणाम समान था और समय विशिष्ट नहीं था।
जजॉओ

यह समझ में आता है - हालांकि यह कोशिश करने के लिए धन्यवाद। तो cat -nएक टैब करता है ? मुझे नहीं पता कि यह कैसे काम करता है।
mikeserv

1
@mikeserv, खुशी से cat -nप्रत्येक lineमें ट्रांसफ़ॉर्म spaces + the number + \t + line- सॉर्ट और कट के लिए आदर्श प्रारूप
JJoao

1

मैंने उपयोग किया है

awk -v BINMODE=rw '!($0 in a){a[$0];print}' infile >> outfile

बिनमोडे = आरडब्ल्यू: लाइन टर्मिनेटर के अंत को खुश रखने के लिए। (मैं एक मिश्रित ओएस वातावरण में रहता हूं)

तर्क सरल है।

यदि वर्तमान रेखा साहचर्य सरणी में नहीं है, तो इसे साहचर्य सरणी में जोड़ें और आउटपुट पर प्रिंट करें।

इस दृष्टिकोण के साथ स्मृति सीमाएं हो सकती हैं। बहुत बड़ी फ़ाइलों और फ़ाइलों के सेट के लिए, मैंने इस पर विभिन्नताओं का उपयोग किया है, सीमाओं को पाने के लिए फ़ाइल संग्रहण का उपयोग किया है।


0

आपकी समस्या के क्रम-संरक्षण वाले शब्दार्थ में एक अद्भुत गुण है: आप समस्या को दूर कर सकते हैं। आप split -l 1000000इनपुट फ़ाइल पर कर सकते हैं ; 1000000-लाइन के टुकड़े जो इसे उत्पन्न करते हैं उनमें लेक्सिकली-ऑर्डर किए गए नाम हैं जो अच्छे हैं; फिर टुकड़ों को uniqify; और फिर (एक दूसरे पास के रूप में) उन लोगों के आउटपुट को एकजुट करता है।

यह बहु-स्तरीय समाधान में बदलने की कीमत पर मेमोरी की समस्या (मेमोरी आवश्यकता को कम करके) को हल करता है।

विशेष रूप से:

इनपुट डेटा उत्पन्न करें:

$ cat make-uniqm-input.py
#!/usr/bin/env python
import random
n = 1000000
for i in xrange(0, n):
    print random.randint(1000, 2000)

$ python make-uniqm-input.py  > uniqm-input.txt

$ wc -l uniqm-input.txt
 1000000 uniqm-input.txt

इनपुट डेटा को विभाजित करें:

$ split -l 10000 uniqm-input.txt

$ ls x?? | head
xaa
xab
xac
xad
xae
xaf
xag
xah
xai
xaj

$ ls x?? | wc -l
     100

$ cat x?? | wc -l
 1000000

सभी पर एक बार uniqifier चलाएं (मेमोरी में सभी अद्वितीय इनपुट लाइनों को बनाए रखता है):

# 'uniqm' is any order-preserving uniq implementation, such as
# gawk '!counts[$0]++'.
$ uniqm < uniqm-input.txt > output-no-splitting.txt

$ wc -l output-no-splitting.txt
    1001 output-no-splitting.txt

स्प्लिट टुकड़ों पर यूनीकिफायर चलाएं (मेमोरी में प्रत्येक टुकड़े से केवल अद्वितीय इनपुट लाइनों को बरकरार रखता है), फिर दूसरे पास के रूप में कम करें:

$ for x in x??; do uniqm < $x; done | uniqm > output-with-splitting.txt

$ wc -l output-with-splitting.txt
    1001 output-with-splitting.txt

की तुलना करें:

$ diff output-no-splitting.txt output-with-splitting.txt

$ head uniqm-input.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

$ head output-with-splitting.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

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


0

एक और दृष्टिकोण (एक अलग जवाब के रूप में पोस्ट करने लायक) है: विभाजन-फ़ाइल दृष्टिकोण के बजाय जो अस्थायी फ़ाइलें बनाता है, स्वयं uniqifier सॉफ़्टवेयर के भीतर बैचिंग करें। उदाहरण के लिए, व्याख्यात्मक उद्देश्यों के लिए रूबी uniqifier कार्यान्वयन का उपयोग करना:

require 'set'
line_batch_count = 50000 # tunable parameter
lines_seen = Set.new
line_number = 0
ARGF.each do |line|
   line_number += 1
   if (line_number % line_batch_count) == 0
     lines_seen.clear
   end
   unless lines_seen.include? line
      puts line
      lines_seen << line
   end
end

विचार हैश-सेट को हर बार साफ़ करना है। फिर यह पुनरावृत्ति बन जाती है:

$ cat uniqm-input.txt | ruby uniqm-capped.rb | wc -l
   20021

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | wc -l
    1001

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | head
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

इसलिए आप इस कैप्ड संस्करण को बार-बार चला सकते हैं, जब तक कि रेखा-गणना एक पुनरावृत्ति से दूसरे में नहीं बदलती।

ध्यान दें कि यह छायांकित- uniqm तकनीक भाषा-स्वतंत्र है: आप lines_seenसरणी को हर N लाइनों को साफ़ कर सकते हैं चाहे आप awk, python, perl, C ++, आदि का उपयोग कर रहे हों; इन सभी भाषाओं के लिए सेट-क्लियर तरीके हैं; मेरा मानना है कि awk's deleteअमानक लेकिन आम है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.