ลบบรรทัดที่ซ้ำกันในขณะที่รักษาลำดับของบรรทัด


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 ไม่มีอะไรอื่นที่จะทำเพื่อให้โหลด อ๊ะยกเลิกหลังจากนั้นสองสามวินาที .. out.txt คือ ~ 1.6 GByte GNU Awk 3.1.7

คำถาม : ฉันจะลบบรรทัดที่ซ้ำกันในขณะที่รักษาลำดับของบรรทัดได้อย่างไร ตัวพิมพ์เล็กก็สำคัญเช่นกัน: "A" และ "a" เป็นสองบรรทัดที่แตกต่างกันต้องเก็บไว้ แต่ "a" และ "a" ซ้ำกันจำเป็นต้องใช้อันแรกเท่านั้น

คำตอบอาจเป็นอะไรก็ได้ .. ถ้า awk ไม่ดีสำหรับสิ่งนี้ .. แล้ว 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]# 

อัปเดต: ฉันลองสิ่งนี้ในเครื่อง RHEL แต่ก็ไม่ได้ยกเลิก แต่ฉันไม่มีเวลารอให้เสร็จแล้วทำไม SL ลินุกซ์ถึงแตกต่างจาก RHEL

อัปเดต: ฉันพยายามใช้ 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

up-to-date rhel และ up-to-date sl linux ไม่ทราบเวอร์ชั่น rhel .. sl คือ: GNU Awk 3.1.7
somelooser28533

ใหญ่แค่out.txtไหน คำสั่งเดียวกันใช้งานได้หรือไม่หากคุณลองใช้ไฟล์ที่เล็กกว่านี้? มีผู้ใช้บนเครื่องกี่คน? มีหน่วยความจำเพียงพอสำหรับกระบวนการหรือไม่ มีสิ่งใดเป็นพิเศษเกี่ยวกับบรรทัด 8547098 ของไฟล์อินพุตหรือไม่
terdon

คำตอบ:


22

ฉันสงสัยว่ามันจะสร้างความแตกต่าง แต่ในกรณีนี่คือวิธีการทำสิ่งเดียวกันใน Perl:

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

หากปัญหาคือการรักษาเส้นที่ไม่ซ้ำกันในหน่วยความจำนั่นจะมีปัญหาเช่นเดียวกับที่awkคุณพยายาม ดังนั้นวิธีการอื่นอาจเป็น:

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

มันทำงานอย่างไร:

  1. ในระบบ GNU cat -nจะใส่หมายเลขบรรทัดต่อท้ายแต่ละบรรทัดตามด้วยช่องว่างจำนวนหนึ่งแล้วตามด้วยอักขระ<tab> ท่อป้อนข้อมูลนี้เพื่อเป็นตัวแทนcatsort

  2. sort's -k2ตัวเลือกสั่งเฉพาะเพื่อพิจารณาตัวละครจากฟิลด์ที่สองจนถึงสิ้นบรรทัดเมื่อเรียงลำดับและsortแยกสาขาโดยเริ่มต้นบนพื้นที่สีขาว(หรือcat' s ช่องว่างแทรกและ<> แท็บ )
    เมื่อตามด้วย-k1nให้sortพิจารณาฟิลด์ที่ 2 เป็นอันดับแรกและจากนั้นอันดับที่สองในกรณีของ-k2เขตข้อมูลที่เหมือนกันจะพิจารณาเขตข้อมูลที่ 1 แต่เรียงตามตัวเลข ดังนั้นจะมีการเรียงแถวซ้ำ ๆ กัน แต่เรียงตามลำดับที่ปรากฏ

  3. ผลที่จะได้ประปาuniq-which จะบอกให้ละเว้นฟิลด์แรก( -f1- และยังเป็นที่คั่นด้วยช่องว่าง) sortและอื่นซึ่งผลในรายการของเส้นที่ไม่ซ้ำกันในไฟล์ต้นฉบับและเป็นประปากลับไป
  4. คราวนี้sortทุกประเภทในเขตข้อมูลแรก( cat's แทรกหมายเลขบรรทัด)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

สวัสดี Terdon OP ต้องการรักษาลำดับของบรรทัดดังนั้นวิธี cat | sort | uniq จะไม่ทำงาน ... เช่นเดียวกับรุ่น perl ของคุณ ...
Lambert

1
เป็นทางออกที่ดีด้วยsort! แต่ส่วนใหญ่sortสามารถทำuniqด้วยตัวเองเพื่อให้คุณสามารถย่อสคริปต์ของคุณได้โดย sort -uk2 | sort -bk1,1n
Costas

@Costas มันมากที่สุดsort? ฉันคิดว่า-uเป็นคุณสมบัติของ GNU
terdon

@don_crissti อ่าขอบคุณมาก ฉันจะใช้ที่นี่ได้อย่างไร ตามที่ฉันเพิ่งสังเกตเห็น (และแก้ไขเพื่อแก้ไข) ฉันต้องเรียงลำดับในฟิลด์ที่ 2 ก่อนและจากนั้นในวันที่ 1 ตัวเลขเพื่อรักษาลำดับบรรทัด ฉันจะใช้-uและระบุว่าควรละเว้นฟิลด์ที่ 1 ได้อย่างไร ตามที่man sortที่-uไม่ได้เป็นหนึ่งในตัวเลือกที่เป็นไปได้สำหรับ-fดังนั้นผมไม่คิดว่ามันสามารถนำมาใช้ที่นี่
terdon

1
นี่คือการแปลง Schwartzian ! (+1)
JJoao

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

กรณีที่ 1 : 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 ค่าที่แตกต่าง:

$ 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 นั้นไม่เหมือนใคร sort -nk1 และ sort -nk1,1 จะส่งคืนผลลัพธ์บางส่วน ฉันลองทั้งสองอย่างผลลัพธ์ก็เหมือนกันและเวลาก็ไม่โดดเด่น
JJoao

ที่เหมาะสม - ขอบคุณสำหรับการลองแม้ว่า ดังนั้นcat -nไม่ได้แท็บ ? ฉันไม่รู้ว่าคำสั่งนั้นทำงานอย่างไร
mikeserv

1
@mikeserv อย่างมีความสุขcat -ntransfrom แต่ละlineใน spaces + the number + \t + line- รูปแบบที่เหมาะสำหรับการจัดเรียงและตัด
JJoao

1

ฉันเคยใช้

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

BINMODE = rw: เพื่อให้จุดสิ้นสุดของจุดสิ้นสุดบรรทัดมีความสุข (ฉันอาศัยอยู่ในสภาพแวดล้อมระบบปฏิบัติการแบบผสม)

ลอจิกง่าย

หากบรรทัดปัจจุบันไม่ได้อยู่ในอาร์เรย์ที่เชื่อมโยงให้เพิ่มเข้าไปในอาร์เรย์ที่เชื่อมโยงแล้วพิมพ์ไปยังเอาต์พุต

อาจมีข้อ จำกัด ของหน่วยความจำด้วยวิธีนี้ สำหรับไฟล์และชุดไฟล์ที่มีขนาดใหญ่มากฉันใช้การเปลี่ยนแปลงนี้โดยใช้ที่เก็บไฟล์เพื่อผ่านข้อ จำกัด


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

รัน uniqifier บนชิ้นส่วนแยก (เก็บเฉพาะบรรทัดอินพุตที่ไม่ซ้ำกันจากแต่ละชิ้นในหน่วยความจำ) จากนั้นลดเป็นครั้งที่สองผ่าน:

$ 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 ตัวอย่างเช่นการใช้ Ruby 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

ดังนั้นคุณสามารถเรียกใช้เวอร์ชันที่ต่อยอดนี้ซ้ำ ๆ ได้จนกระทั่งจำนวนบรรทัดไม่เปลี่ยนจากการทำซ้ำหนึ่งไปเป็นอีกครั้ง

โปรดทราบว่าเทคนิค capped-uniqm นี้เป็นภาษาที่ไม่ขึ้นกับภาษา: คุณสามารถล้างlines_seenอาเรย์ทุกบรรทัด N ไม่ว่าคุณจะใช้ awk, python, perl, C ++ ฯลฯ มีวิธีการที่ชัดเจนสำหรับภาษาเหล่านี้ทั้งหมด ผมเชื่อว่าawk's deleteเป็นที่ไม่ได้มาตรฐาน แต่ที่พบบ่อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.