Satırların sırasını koruyarak yinelenen satırları kaldırın


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

"" Sunucu "": 8 GByte RAM + 16 GByte SWAP, x> 300 GByte boş alan, amd64, masaüstü CPU. Bilimsel Linux 6.6. LOAD yapmak için başka hiçbir şey üzerinde çalışmaz. Awk birkaç saniye sonra durur .. out.txt ~ 1.6 GByte. GNU Awk 3.1.7.

Soru : Satırların sırasını korurken yinelenen satırları nasıl kaldırabilirim? Durum da önemlidir, örneğin: "A" ve "a" iki farklı çizgidir, tutmak zorunda. Ancak "a" ve "a" birbirinin aynısıdır, sadece ilki gereklidir.

Cevap herhangi bir şey olabilir .. awk bunun için iyi değilse .. o zaman perl / sed .. sorun ne olabilir?

[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]# 

Güncelleme: Bunu bir RHEL makinesinde denedim, durmuyor, ancak bitmesini beklemek için zamanım olmadı .. SL linux neden RHEL'den farklı değil?

Güncelleme: Ubuntu 14 sanal tahminleri deniyorum .. şimdiye kadar çalışıyor! Bu bir ulimit sorunu değil: 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
Örneğinizde yinelenen satır yok ...?
mikeserv

1
awkİki makinedeki sürümler nelerdir ?
cuonglm

güncel rhel ve güncel sl linux, rhel sürümünü bilmiyorum .. sl olduğunu: GNU Awk 3.1.7
somelooser28533

Ne kadar büyük out.txt? Daha küçük bir dosyada denerseniz aynı komut çalışır mı? Makinede kaç kullanıcı var? İşlem için yeterli bellek var mıydı? Giriş dosyasının 8547098 satırıyla ilgili özel bir şey var mı?
terdon

Yanıtlar:


22

Bir fark yaratacağından şüpheliyim, ancak her ihtimale karşı, Perl'de aynı şeyi nasıl yapacağınız aşağıda açıklanmıştır:

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

Sorun, benzersiz satırları bellekte tutuyorsa, awkdenediğinizle aynı soruna sahip olacaktır . Başka bir yaklaşım şöyle olabilir:

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

Nasıl çalışır:

  1. Bir GNU sisteminde, cat -nsatır sayısını her satırın önüne bir miktar boşluk izleyerek ve ardından bir <tab> karakteri izler . catbu giriş gösterimini oluşturur sort.

  2. sortbireyin -k2seçenek sıralarken sadece satırın sonuna kadar ikinci alandan karakterleri dikkate talimatı verir ve sortbeyaz boşluk varsayılan olarak böler alanları (veya cat'boşluk ve eklenen s <sekmesi> ) .
    Ardından -k1n, sortönce 2. alanı, daha sonra ikinci olarak - özdeş -k2alanlar söz konusu olduğunda , 1. alanı dikkate alır ancak sayısal olarak sıralanır. Böylece tekrarlanan satırlar birlikte sıralanacak, ancak göründükleri sırayla.

  3. Sonuçlar -uniq ilk alanı ( -f1ve boşlukla ayrılmış olarak görmezden geldiği söylenir ) - ve orijinal dosyadaki benzersiz satırların bir listesi ile sonuçlanır ve geri alınır sort.
  4. Bu kez sort, ilk alanı ( cateklenen satır numarası) sayısal olarak sıralar, sıralama düzenini orijinal dosyadaki haline geri getirir ve bu sonuçları buna bağlar cut.
  5. Son olarak, cuttarafından eklenen satır numaralarını kaldırır cat. Bu, cutyalnızca 2. alandan satır sonuna kadar yazdırılarak gerçekleştirilir (ve cutvarsayılan sınırlayıcısı bir <tab> karakteridir) .

Örneklemek gerekirse:

$ 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

Merhaba Terdon, OP satırların sırasını korumak gerekir, bu yüzden kedi | sort | uniq yöntemi işe yaramayacak ... Perl sürümü gibi olsa ...
Lambert

1
Güzel bir çözüm sort! Ama çoğu kendi başına sortyapabilir uniq, böylece senaryoyu sort -uk2 | sort -bk1,1n
Costas

@Costas en çok sortmu? -uBir GNU özelliği olduğunu düşündüm .
terdon

@ don_crissti ah, öyle, teşekkürler. Yine de burada nasıl kullanabilirim? Ben sadece fark ettim (ve düzeltmek için düzenlenmiş), satır sırasını korumak için önce 2. alanında ve sonra 1. sayısal olarak sıralamak gerekir. Daha sonra -ubu alanı 1. alanı göz ardı etmesi gerektiğini nasıl belirleyebilirim? Göre man sort, -uolası seçeneklerden biri değil -fBuranın kullanılabilir sanmıyorum.
terdon

1
Bu Schwartz dönüşümidir ! (+1)
JJoao

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

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

EDIT 1: Gerçekten işe yarıyor mu? (karşılaştırma)

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

Durum 1 : 100_000_000 rasgele sayılar (5 basamaklı her biri), 566Mbytes, 31_212 farklı değerler:

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

Durum 2 : 50_000_000 rand sayısı (her biri 10 basamaklı), 516Mbyte, 48_351_464 farklı değer:

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

(aşağıdaki sayılar çok kesin değildir):

┌────────┬────────┬────────────────┬────────┬──────┐
         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               
└────────┴────────┴────────────────┴────────┴──────┘

önbellekli 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 }

Sıralama, önbellek boyutu seçeneği eklenerek de optimize edilebilir (yapılmadı).

Hızlı bir sonuç:

  • sort harika bir komut!

1
sort -uk2ve sort -nk1,1farklı. Birincisi 2cd anahtarından satırın sonuna kadar, ikincisi sadece ilk anahtarı dikkate alır . sort -nk1Oradan değiştirmelisin - bu şekilde daha da hızlı olabilir, ama kesinlikle daha güvenilir olacak. Bu arada - bunlar güzel kutular.
mikeserv

@mikeserv, yorum için teşekkürler. K1,1 benzersiz olduğundan, sort -nk1 ve sort -nk1,1 bazı sonuçları döndürür. Her ikisini de denedim, sonuç aynıydı ve zaman farklı değildi.
JJoao

Bu mantıklı - yine de denediğiniz için teşekkürler. Yani cat -nbir does sekmeyi ? Bu komutun nasıl çalıştığını bilmiyorum.
mikeserv

1
@mikeserv, mutlu cat -nher Transfrom lineiçinde spaces + the number + \t + linetür ve kesim için ideal bir formatta -
JJoao

1

Kullandım

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

BINMODE = rw: satır sonu sonlandırıcılarını mutlu tutmak için. (Karışık işletim sistemi ortamında yaşıyorum)

Mantık basittir.

Geçerli satır ilişkilendirilebilir dizide değilse, ilişkilendirilebilir diziye ekleyin ve çıktıya yazdırın.

Bu yaklaşımla bellek sınırlamaları olabilir. Çok büyük dosyalar ve dosya kümeleri için, bu sınırlamaları aşmak için dosya depolama alanını kullanarak buradaki varyasyonları kullandım.


0

Sorununuzun sipariş koruma semantiği harika bir özelliğe sahiptir: sorunu alt bölümlere ayırabilirsiniz. Yapabileceğin split -l 1000000giriş dosyada; ürettiği 1000000 satırlık parçaların sözlük sıralaması iyi olan adları vardır; sonra parçaları uniqify; ve sonra (ikinci geçiş olarak) bunların çıktılarını tek tek ayırın.

Bu, çok yollu bir çözüme dönüştürülme pahasına bellek yetersiz sorununu (bellek gereksinimini karşılayarak) çözer.

özellikle:

Girdi verileri üretin:

$ 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

Giriş verilerini ayırın:

$ 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'ı bir kerede çalıştırın (bellekteki tüm benzersiz giriş satırlarını tutar):

# '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'ı bölünmüş parçalar üzerinde çalıştırın (bellekteki her parçadan yalnızca benzersiz giriş satırlarını tutar), sonra ikinci geçiş olarak azaltın:

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

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

Karşılaştırmak:

$ 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

Girişinizdeki benzersiz ve benzersiz olmayan satırların oranını veya giriş satırlarının ne kadar iyi karıştığını bilmiyorum - bu yüzden ihtiyacınız olan bölünmüş dosya sayısı açısından yapılması gereken bazı ayarlamalar var.


0

Başka bir yaklaşım (ayrı bir yanıt olarak göndermeye değer): geçici dosyalar oluşturan bölünmüş dosya yaklaşımı yerine, tekil yazılımın içinde toplu işlemi yapın. Örneğin, açıklayıcı amaçlar için bir Ruby uniqifier uygulaması kullanma:

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

Fikir, karma kümesini sık sık temizlemek. Sonra bu yinelemeli hale gelir:

$ 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

Böylece, satır sayısı bir yinelemeden diğerine değişmeyene kadar bu sınırlı sürümü tekrar tekrar çalıştırabilirsiniz.

Bu başlıklı uniqm tekniğinin dilden bağımsız lines_seenolduğuna dikkat edin: awk, python, perl, C ++ vb. İnanıyorum awks' deletestandart dışı ama yaygındır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.