Unix'te sıralamadan bir dosyadaki yinelenen satırlar nasıl silinir?


137

Unix'te bir dosyadaki yinelenen satırları silmenin bir yolu var mı?

Bunu sort -uve uniqkomutlarıyla yapabilirim, ancak sedveya kullanmak istiyorum awk. Mümkün mü?


12
Ardışık kopyaları kastediyorsanız uniq, o zaman tek başına yeterlidir.
Michael Krelin - hacker

ve aksi takdirde bunun mümkün olduğuna inanıyorum awk, ancak daha büyük dosyalarda oldukça kaynak tüketecek.
Michael Krelin - hacker

Yinelenen stackoverflow.com/q/24324350 ve stackoverflow.com/q/11532157 , ideal olarak buraya taşınması gereken ilginç yanıtlara sahiptir.
üçlü

Yanıtlar:


290
awk '!seen[$0]++' file.txt

seenAwk'nin dosyanın her satırını ileteceği ilişkilendirilebilir bir dizidir. Dizide bir satır yoksa, seen[$0]yanlış olarak değerlendirilir. !Mantıksal DEĞİL operatörüdür ve true false tersine çevirir. Awk, ifadenin doğru olarak değerlendirildiği satırları yazdırır. ++Artışlarla seenböylece seen[$0] == 1sonra ilk kez bir satır ve daha sonra bulunan seen[$0] == 2, ve böyle devam eder.
Awk, 0ve ""(boş dize) dışındaki her şeyi doğru olarak değerlendirir. Yinelenen bir çizgi yerleştirilirse seeno zaman !seen[$0]false olarak değerlendirecektir ve çizgi çıktı yazılır edilmeyecektir.


5
Bir dosyaya kaydetmek için bunu yapabilirizawk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal

5
Burada önemli bir uyarı: Bunu birden fazla dosya için yapmanız gerekiyorsa ve komutun sonunda daha fazla dosya tıklarsanız veya bir joker karakter kullanırsanız… 'görülen' dizisi, TÜM dosyalardan çift satırlarla dolacaktır. Bunun yerine her dosyayı bağımsız olarak ele almak istiyorsanız, şöyle bir şey yapmanız gerekirfor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9

@ NickK9, birden fazla dosyada kümülatif olarak çoğaltmanın kendi başına harika. Güzel ipucu
sfscs

31

Gönderen http://sed.sourceforge.net/sed1line.txt : (nasıl bu işleri bana sormayın lütfen ;-))

 # delete duplicate, consecutive lines from a file (emulates "uniq").
 # First line in a set of duplicate lines is kept, rest are deleted.
 sed '$!N; /^\(.*\)\n\1$/!P; D'

 # delete duplicate, nonconsecutive lines from a file. Beware not to
 # overflow the buffer size of the hold space, or else use GNU sed.
 sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'

geekery ;-) +1, ancak kaynak tüketimi kaçınılmazdır.
Michael Krelin - hacker

3
! '$ N; /^(.*)\n\1$/!P; D 'şu anlama gelir: "Son satırda değilseniz, başka bir satırda okuyun. Şimdi neye sahip olduğunuza bakın ve ardından bir satırsonu ile gelen şeyler ISN'T ve sonra aynı şeyleri tekrar yazdırın. Şimdi silin şeyler (yeni satıra kadar). "
Beta

2
'G; s / \ N / && /; / ^ ([- ~] * \ n). * \ n \ 1 / d; s / \ N //; h; P 'kabaca şu anlama gelir: "Tüm ayırma alanını bu satıra ekle, o zaman yinelenen bir satır görürseniz tüm şeyi atarsanız, aksi halde tüm karışıklığı ayırma alanına kopyalayın ve ilk bölümü yazdırın (bu satır okuyun. "
Beta

$!kısım gerekli? sed 'N; /^\(.*\)\n\1$/!P; D'Aynı şeyi yapmıyor mu ? Makinemde ikisinin farklı olduğu bir örnek bulamıyorum (fwiw her iki sürümün sonunda boş bir satır denedim ve her ikisi de iyi).
eddi

1
Neredeyse 7 yıl sonra ve kimse @amichair'i yanıtlamadı ... <sniff> beni üzüyor. ;) Her neyse, [ -~]0x20 (boşluk) ile 0x7E (tilde) arasındaki bir ASCII karakter aralığını temsil eder. Bunlar kabul edilir yazdırılabilir ASCII karakterleri (bağlantılı sayfa ayrıca 0x7F / silme vardır ama bu doğru görünmüyor). Bu, ASCII kullanmayan veya sekme karakterleri kullanmayanlar için çözümü bozar. Daha taşınabilir olan çok daha fazla karakter içerir… aslında biri hariç hepsi. [^\n]
B Katmanı

14

@ Jonas'ın awk çözümüne benzer Perl tek satırlık:

perl -ne 'print if ! $x{$_}++' file

Bu varyasyon, karşılaştırmadan önce sondaki beyaz boşluğu kaldırır:

perl -lne 's/\s*$//; print if ! $x{$_}++' file

Bu varyasyon, dosyayı yerinde düzenler:

perl -i -ne 'print if ! $x{$_}++' file

Bu varyasyon, dosyayı yerinde düzenler ve bir yedek oluşturur file.bak

perl -i.bak -ne 'print if ! $x{$_}++' file

6

Yukarıda Andre Miller'ın gönderdiği tek satırlık, giriş dosyası boş bir satırla ve karakter olmadan sona erdiğinde sed'in son sürümleri dışında çalışır. Mac'imde CPU'm sadece dönüyor.

Son satır boşsa ve karakter içermiyorsa sonsuz döngü :

sed '$!N; /^\(.*\)\n\1$/!P; D'

Takılmıyor ama son satırı kaybediyorsun

sed '$d;N; /^\(.*\)\n\1$/!P; D'

Açıklama sed SSS'nin en sonunda yer almaktadır :

GNU sed bakımcısı
, bunun neden olabileceği taşınabilirlik sorunlarına rağmen,
model alanını yazdırmak için ( silmek yerine ) N komutunu değiştirmenin,
"Sonraki satırı ekleme" komutunun nasıl davranması gerektiğine dair kişinin sezgileriyle daha tutarlı olduğunu hissetti .
Değişikliği destekleyen bir başka gerçek de, "{N; command;}"
dosyasında tek sayıda satır varsa son satırı silecek, ancak dosyanın çift sayıda
satırı varsa son satırı yazdıracak olmasıdır.

N'nin önceki davranışını (EOF'ye
ulaştıktan sonra desen alanını silme) kullanan komut dosyalarını sed'in
tüm sürümleriyle uyumlu komut dosyalarına dönüştürmek için tek bir "N" değiştirin; "$ d; N;" .


5

Vim (Vi uyumlu) kullanmanın alternatif bir yolu :

Bir dosyadan yinelenen, ardışık satırları silin:

vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq

Bir dosyadan yinelenen, ardışık olmayan ve boş olmayan satırları silin:

vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq


4

İlk çözüm ayrıca http://sed.sourceforge.net/sed1line.txt dosyasındandır.

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr '$!N;/^(.*)\n\1$/!P;D'
1
2
3
4
5

ana fikir şudur:

print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.

açıklar:

  1. $!N;: mevcut satır son satır DEĞİLSE N, sonraki satırı okumak için komutu kullanın pattern space.
  2. /^(.*)\n\1$/!P: Akımının içeriğini eğer pattern spaceiki duplicate stringayrılmış \nbir sonraki çizgi anlamına gelir, samemevcut satır bizim temel fikrine göre, de Yazdıramıyor; aksi halde, geçerli satırı anlamına gelen onun yinelenen ardışık hatlarının tümünün SON görünümü, biz şimdi kullanabilirsiniz Pakım karakter yazdırmak için komutu pattern spaceutil \n( \nayrıca yazdırılır).
  3. D: Kullandığımız Dakım karakter silmek için komut pattern spaceutil \n( \ndaha sonra içeriği de silindi) pattern spacesonraki çizgidir.
  4. ve Dkomut zorlar sedonun atlamak için FIRSTkomuta $!Nancak dosya veya standart girdi akışından sonraki satırını okumak DEĞİL.

İkinci çözümün anlaşılması kolaydır (kendimden):

$ echo -e '1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5' |sed -nr 'p;:loop;$!N;s/^(.*)\n\1$/\1/;tloop;D'
1
2
3
4
5

ana fikir şudur:

print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.

açıklar:

  1. giriş akışından veya dosyadan yeni bir satır okuyun ve bir kez yazdırın.
  2. Kullanım :loopkomutu bir seti labeladında loop.
  3. kullanmak Ndoğru bir sonraki satırını okumak için pattern space.
  4. ile s/^(.*)\n\1$/\1/mevcut satırı silmek için sonraki satır mevcut satırla aynıysa s, deleteeylemi yapmak için komut kullanırız .
  5. eğer skomut başarılı bir şekilde çalıştırılırsa, o zaman adlandırılmışa atlamak için tloopkomut kuvvetini sedkullanın , bu aynı döngüyü sonraki satırlara yapacak olan satırın ardışık yinelenen satırları yoktur ; aksi takdirde, satırla aynı olan komutu kullanın ve ilk komuta atlamaya zorlayın , bu komuttur, akımın içeriği sonraki yeni satırdır.labellooplatest printedDdeletelatest-printed linesedppattern space

busybox ile Windows'ta aynı komut:busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
scavenger

-1

Bu, awk
Under Line kullanılarak elde edilebilir , benzersiz Değerler gösterecektir.

awk file_name | uniq

Bu benzersiz değerleri yeni bir dosyaya verebilirsiniz

awk file_name | uniq > uniq_file_name

yeni dosya uniq_file_name sadece Benzersiz değerleri içerecek, kopya olmayacak


-4
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'

Awk kullanarak yinelenen satırları siler.


1
Bu, hatların sırasını bozacaktır.
Vijay

1
20 GB metin dosyası nedir? Çok yavaş.
Alexander Lubyagin

Her zamanki gibi, hiçbir işe yaramaz. Her neyse, bunu zaten kendi başına yapıyor ve girdinin satır başına tam olarak bir kelime olmasını gerektirmiyor. catuniq
üçlü
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.