Sütuna göre 'uniq' yapmanın bir yolu var mı?


195

Bunun gibi bir .csv dosyası var:

stack2@example.com,2009-11-27 01:05:47.893000000,example.net,127.0.0.1
overflow@example.com,2009-11-27 00:58:29.793000000,example.net,255.255.255.0
overflow@example.com,2009-11-27 00:58:29.646465785,example.net,256.255.255.0
...

Dosyadan (yani overflow@example.comyukarıdaki örnekte bulunan satırlardan biri) yinelenen e-postaları (tüm satır) kaldırmak zorundayım . Nasıl kullanırım uniq(virgülle ayırarak) tek alanda 1? Buna göre man, uniqsütunlar için seçenekler yoktur.

Bir şey denedim sort | uniqama işe yaramıyor.

Yanıtlar:


327
sort -u -t, -k1,1 file
  • -u benzersiz için
  • -t, yani virgül ayırıcıdır
  • -k1,1 anahtar alan için 1

Test sonucu:

overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 

3
sütun virgül içeriyorsa bu çalışmaz (alıntı ile)
user775187

13
neden -k1,1'de 1'e ihtiyacınız var? neden sadece -k1 değil?
hello_there_andy

18
@hello_there_andy: Bu kılavuzda ( man sort) açıklanmıştır . Başlangıç ​​ve bitiş pozisyonunu temsil eder.
Serrano

3
@CarlSmotricz: Test ettim ve onu doğruladı sort: ' 'ın manpage diyor -u, --unique ile -c, sıkı sipariş için çek; olmaksızın -c, eşit çalışma sadece ilk çıkışı .' Yani, gerçekten de "yinelemenin sıralamadan önceki ilk hali" dir.
Geremia

2
bu satırların sırasını da değiştirir, değil mi?
rkachach

104
awk -F"," '!_[$1]++' file
  • -F alan ayırıcısını ayarlar.
  • $1 ilk alandır.
  • _[val]valkarma _(normal bir değişken) arar .
  • ++ artırın ve eski değeri döndürün.
  • ! mantıksal değil döndürür.
  • sonunda örtülü bir baskı var.

4
Bu yaklaşım sıralamadan iki kat daha hızlı
bitek

9
Bu ayrıca hatları orijinal sırada tutmanın ek avantajına da sahiptir!
AffluentOwl

8
İlk yerine son uniq'e ihtiyacınız varsa, bu awk betiği yardımcı olacaktır:awk -F',' '{ x[$1]=$0 } END { for (i in x) print x[i] }' file
Sukima

4
@eshwar sadece sözlük dizinine daha fazla alan ekleyin! Örneğin !_[$1][$2]++, ilk iki alana göre sıralamak için kullanılabilir. Benim awk-fu, bir dizi alanda benzersiz olacak kadar güçlü değil. :(
Soham Chowdhury

1
Parlak! bu seçenek cevaptan daha iyidir çünkü satır sırasını korur
rkachach

16

Birden çok sütunu dikkate almak.

Sütun 1 ve sütun 3'e göre sıralayın ve benzersiz liste verin:

sort -u -t : -k 1,1 -k 3,3 test.txt
  • -t : kolon ayırıcıdır
  • -k 1,1 -k 3,3 sütun 1 ve sütun 3'e göre

8

veya uniq kullanmak istiyorsanız:

<mycvs.cvs tr -s ',' ' ' | awk '{print $3" "$2" "$1}' | uniq -c -f2

verir:

1 01:05:47.893000000 2009-11-27 tack2@domain.com
2 00:58:29.793000000 2009-11-27 overflow@domain2.com
1

5
Olası bir basitleştirmeye işaret etmek istiyorum: Sen-ebilmek dökmek cat! Tr içine borulama yerine, sadece tr kullanarak dosyayı okuyun <. Borulama cat, acemiler tarafından kullanılan yaygın bir gereksiz komplikasyondur. Büyük miktarda veri için elde edilecek bir performans etkisi vardır.
Carl Smotricz

4
Bunu bildiğim iyi oldu. Teşekkürler! (Tabii ki bu mantıklı, "kedi" ve "tembelliği" düşünerek;))
Carsten C.

Alanların ters çevrilmesi ile basitleştirilebilir rev.
Hielke Walinga

5

Kullanabileceğiniz kopyaların sonuncusunu korumak istiyorsanız

 tac a.csv | sort -u -t, -r -k1,1 |tac

Bu benim ihtiyacımdı

buraya

tac dosyayı satır satır tersine çevirir


1

İşte çok şık bir yol.

Öncelikle içeriği benzersizlik için karşılaştırılacak sütun sabit bir genişlik olacak şekilde biçimlendirin. Bunu yapmanın bir yolu, alan / sütun genişliği belirleyicisiyle ("% 15s") awk printf kullanmaktır.

Artık uniq'in -f ve -w seçenekleri, önceki alanları / sütunları atlamak ve karşılaştırma genişliğini (sütun (lar) genişliği) belirtmek için kullanılabilir.

İşte üç örnek.

İlk örnekte ...

1) İlgilenilen sütunu geçici olarak alanın maksimum genişliğinden daha büyük veya ona eşit bir sabit genişlik yapın.

2) Önceki sütunları atlamak için -f uniq seçeneğini kullanın ve genişliği tmp_fixed_width ile sınırlamak için -w uniq seçeneğini kullanın.

3) Genişliğini "geri yüklemek" için sondaki boşlukları sütundan kaldırın (önceden boşluk bırakılmadığı varsayılarak).

printf "%s" "$str" \
| awk '{ tmp_fixed_width=15; uniq_col=8; w=tmp_fixed_width-length($uniq_col); for (i=0;i<w;i++) { $uniq_col=$uniq_col" "}; printf "%s\n", $0 }' \
| uniq -f 7 -w 15 \
| awk '{ uniq_col=8; gsub(/ */, "", $uniq_col); printf "%s\n", $0 }'

İkinci örnekte ...

Yeni bir uniq sütunu oluşturma 1. Ardından uniq filtresi uygulandıktan sonra kaldırın.

printf "%s" "$str" \
| awk '{ uniq_col_1=4; printf "%15s %s\n", uniq_col_1, $0 }' \
| uniq -f 0 -w 15 \
| awk '{ $1=""; gsub(/^ */, "", $0); printf "%s\n", $0 }'

Üçüncü örnek ikinciyle aynıdır, ancak birden çok sütun için.

printf "%s" "$str" \
| awk '{ uniq_col_1=4; uniq_col_2=8; printf "%5s %15s %s\n", uniq_col_1, uniq_col_2, $0 }' \
| uniq -f 0 -w 5 \
| uniq -f 1 -w 15 \
| awk '{ $1=$2=""; gsub(/^ */, "", $0); printf "%s\n", $0 }'

-3

sütunu awk ile izole etmekten daha basit, belirli bir dosya için belirli bir değere sahip her şeyi kaldırmanız gerekiyorsa, neden sadece grep -v yapmıyorsunuz:

örneğin, ikinci sıra satırında "col2" değerine sahip her şeyi silmek için: col1, col2, col3, col4

grep -v ',col2,' file > file_minus_offending_lines

Bu yeterince iyi değilse, bazı satırlar muhtemelen eşleşen değer farklı bir sütunda görünerek yanlış şekilde soyulabileceğinden, şöyle bir şey yapabilirsiniz:

Sorunlu sütunu izole etmek için awk: eg

awk -F, '{print $2 "|" $line}'

-F, "," ile sınırlandırılmış alanı ayarlar, $ 2, sütun 2, ardından bazı özel sınırlayıcı ve ardından tüm satır anlamına gelir. Ardından , rahatsız edici değerle başlayan satırları kaldırarak filtreleyebilirsiniz :

 awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE

ve sonra ayırıcıdan önce bir şeyler çıkarın:

awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE | sed 's/.*|//g'

(note -the sed komutu özensizdir çünkü kaçan değerleri içermez. Ayrıca sed kalıbı gerçekten "[^ |] +" gibi bir şey olmalıdır (yani sınırlayıcı olmayan herhangi bir şey). Umarım bu yeterince açıktır.


3
Hatları temizlemek istemiyor, belirli bir dize ile bir satırın tek bir kopyasını tutmak istiyor. Uniq doğru kullanım durumudur.
15:34

-3

sortÖnce dosyayı sıralayarak, uygulayabilirsiniz uniq.

Dosyayı gayet iyi sıralıyor gibi görünüyor:

$ cat test.csv
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

$ sort test.csv | uniq
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1

Ayrıca AWK büyüsü de yapabilirsiniz:

$ awk -F, '{ lines[$1] = $0 } END { for (l in lines) print lines[l] }' test.csv
stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1
overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 

Bu, soruda istendiği gibi sütuna göre benzersiz değildir . Bu sadece tüm çizgi için benzersizdir. Ayrıca, bir uniq yapmak için bir tür yapmak zorunda değilsiniz. İkisi birbirini dışlar.
Javid Jamae

1
Evet haklısın. Son örnek, kabul edilen cevap çok daha temiz olsa da, sorunun ne istediğini yapar. İlgili sortardından uniq, sortyapmadan önce yapılması gereken uniqbu iş yapmaz aksi (ama ikinci komuta ve sadece kullanımını atlayabilirsiniz sort -u). Kimden uniq(1): "Bitişik eşleşen satırları INPUT'dan (veya standart girişten) süzerek OUTPUT'a (veya standart çıktıya) yazın."
Mikael S

Ah, uniq'ten önce sıralama yapmakta haklısın. Uniq'in sadece bitişik hatlarda çalıştığını hiç fark etmedim. Her zaman sadece sort -u kullanırım.
Javid Jamae
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.