Bir dosyadaki yinelenen satırları silmeden tanımlansın mı?


11

Ben uzun bir giriş listesi ile bir metin dosyası olarak benim referanslarım var ve her iki (veya daha fazla) alanı vardır.

İlk sütun, referansın url'sidir; ikinci sütun, girişin nasıl yapıldığına bağlı olarak biraz değişebilen başlıktır. Var olan veya olmayan üçüncü alan için aynıdır.

Tanımlamak istiyorum ama aynı ilk alan (başvuru url) olan girişleri kaldırmak değil. Ben biliyorum sort -k1,1 -uama bu otomatik olarak (etkileşimli olmayan) ilk hit hariç hepsini kaldıracak. Hangisini tutacağımı seçebilmem için bana haber vermenin bir yolu var mı?

Aynı ilk alana ( http://unix.stackexchange.com/questions/49569/) sahip üç satırın altındaki alıntıda , ek etiketler (sıralama, CLI) olduğundan ve satır 1 ve # 3'ü silmek için satır 2'yi tutmak istiyorum:

http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Bu tür “kopyaları” tanımlamaya yardımcı olacak bir program var mı? Sonra, 1 ve 3 numaralı satırları kişisel olarak silerek manuel olarak temizleyebilir miyim?


Örneğinizi tam olarak anlamıyorum ... girdinin ve beklenen çıktının daha basitleştirilmiş bir sürümünü verebilir misiniz?
Oli

Lütfen şimdi daha net olup olmadığına bakın?
DK Bose

Yanıtlar:


9

Sorunuzu anlarsam, şöyle bir şeye ihtiyacınız olduğunu düşünüyorum:

for dup in $(sort -k1,1 -u file.txt | cut -d' ' -f1); do grep -n -- "$dup" file.txt; done

veya:

for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n -- "$dup" file.txt; done

file.txthakkınızda veri içeren dosyanız nerede ilgileniyor.

Çıktıda, ilk alanın iki veya daha fazla olduğu satır ve çizgi sayısını göreceksiniz.


3
Teşekkür ederim: cut -d " " -f1 file.txt | uniq -dbana güzel bir çıktı bile veriyor.
DK Bose

@DKBose Muhtemelen daha fazla olasılık var, ama ben de senin emrini kullanmak istedim.
Radu Rădeanu

Teşekkürler. İkinci komut sevdiğim komut. İlkini kaldırabilirsiniz. Ve bu da güzel olurdu kodu
DK Bose

10

Bu, uniqkomutla çözülebilen klasik bir sorundur . art ardauniq yinelenen satırları algılayabilir ve yinelenenleri kaldırabilir ( , ) veya yalnızca yinelenenleri tutabilir ( , ).-u--unique-d--repeated

Yinelenen satırların siparişi sizin için önemli olmadığından, önce sıralamanız gerekir. Ardından uniqyalnızca benzersiz satırları yazdırmak için kullanın :

sort yourfile.txt | uniq -u

Ayrıca, seçenek için kopya sayısını basan bir -c( --count) seçeneği de vardır -d. Ayrıntılar için kılavuz sayfasına uniqbakınız.


İlk alandan sonraki parçaları gerçekten önemsemiyorsanız, yinelenen anahtarları bulmak ve her satır numarasını yazdırmak için aşağıdaki komutu kullanabilirsiniz ( | sort -nçıktının satırlara göre sıralanması için başka bir komut ekleyin ):

 cut -d ' ' -f1 .bash_history | nl | sort -k2 | uniq -s8 -D

Yinelenen satırları görmek istediğinizden (ilk alanı anahtar olarak kullanarak), doğrudan kullanamazsınız uniq. Otomasyonu zorlaştıran sorun, başlık bölümlerinin değişmesidir, ancak bir program hangi başlığın son olarak kabul edilmesi gerektiğini otomatik olarak belirleyemez.

Burada, script.awkmetin dosyanızı giriş olarak alan ve tüm yinelenen satırları yazdırmak için hangisini sileceğinize karar verebileceğiniz bir AWK betiği (kaydedin ). ( awk -f script.awk yourfile.txt)

#!/usr/bin/awk -f
{
    # Store the line ($0) grouped per URL ($1) with line number (NR) as key
    lines[$1][NR] = $0;
}
END {
    for (url in lines) {
        # find lines that have the URL occur multiple times
        if (length(lines[url]) > 1) {
            for (lineno in lines[url]) {
                # Print duplicate line for decision purposes
                print lines[url][lineno];
                # Alternative: print line number and line
                #print lineno, lines[url][lineno];
            }
        }
    }
}

Bu istediğim yakın olduğunu düşünüyorum ama -f, --skip-fields = N (ilk N alanları karşılaştırmak kaçının) tersine ihtiyacım var. Başka bir deyişle, sadece ilk alanın, url'lerin dikkate alınmasını istiyorum.
DK Bose

@DKBose Sabit sayıda karakterle sınırlamak için bir -w( --check-chars) seçeneği vardır , ancak örneğinizi gördüğünüzde değişken ilk alanlarınız vardır. Yana uniqsaha seçimini desteklemez, bir geçici çözümü kullanmak zorunda. Daha kolay olduğu için bir AWK örneği ekleyeceğim.
Lekensteyn

Evet, sadece bakıyordum -wama ilk alanın uzunluğu değişkendir :(
DK Bose

@DKBose Lütfen en son düzenlemeye bakın
Lekensteyn

1
Awk: script.awk: line 4: sözdizimi hatası veya yakınında [awk: script.awk: line 10: sözdizimi hatası veya yakınında [awk: script.awk: line 18: sözdizimi hatası at veya near}
DK Bose

2

Bunu doğru okursam, tek ihtiyacın olan şey

awk '{print $1}' file | sort | uniq -c | 
    while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done

Bu, dupe içeren satırın numarasını ve satırın kendisini yazdırır. Örneğin, bu dosyayı kullanarak:

foo bar baz
http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
bar foo baz
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
baz foo bar
http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Bu çıktıyı üretecek:

2:http://unix.stackexchange.com/questions/49569/  unique-lines-based-on-the-first-field
4:http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field   sort, CLI
6:http://unix.stackexchange.com/questions/49569/  Unique lines based on the first field

Yalnızca satır sayısını yazdırmak için şunları yapabilirsiniz:

awk '{print $1}' file | sort | uniq -c | 
 while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 1

Ve sadece satırı yazdırmak için:

awk '{print $1}' file | sort | uniq -c | 
while read num dupe; do [[ $num > 1 ]] && grep -n -- "$dupe" file; done | cut -d: -f 2-

Açıklama:

awkBetiğide 1 uzay dosyasının alanını ayrılmış yazdırır. $NN'inci alanı yazdırmak için kullanın . sortsıralar ve uniq -cher satırın oluşumunu sayar.

Bu daha sonra geçirilir whileolarak yineleme sayısını kaydeder döngü $numgibi çizgi $dupeve eğer $numo kullanarak, o satır için dosyayı arayacaktır (o zamanlar en azından çoğaltılamaz oluyor böylece) birden büyük olan -nhat numarasını yazdırmak için. Aşağıdakilerin --, grepne zaman $dupebaşlayabileceği için yararlı olan bir komut satırı seçeneği olmadığını söyler -.


1

Şüphesiz, listedeki en ayrıntılı olan muhtemelen daha kısa olabilir:

#!/usr/bin/python3
import collections
file = "file.txt"

def find_duplicates(file):
    with open(file, "r") as sourcefile:
        data = sourcefile.readlines()
    splitlines = [
        (index, data[index].split("  ")) for index in range(0, len(data))
        ]
    lineheaders = [item[1][0] for item in splitlines]
    dups = [x for x, y in collections.Counter(lineheaders).items() if y > 1]
    dupsdata = []
    for item in dups:
        occurrences = [
            splitlines_item[0] for splitlines_item in splitlines\
                       if splitlines_item[1][0] == item
            ]
        corresponding_lines = [
            "["+str(index)+"] "+data[index] for index in occurrences
            ]
        dupsdata.append((occurrences, corresponding_lines))

    # printing output   
    print("found duplicates:\n"+"-"*17)
    for index in range(0, len(dups)):
        print(dups[index], dupsdata[index][0])
        lines = [item for item in dupsdata[index][1]]
        for line in lines:
            print(line, end = "")


find_duplicates(file)

şöyle bir metin dosyasına verir:

monkey  banana
dog  bone
monkey  banana peanut
cat  mice
dog  cowmeat

gibi bir çıktı:

found duplicates:
-----------------
dog [1, 4]
[1] dog  bone
[4] dog  cowmeat
monkey [0, 2]
[0] monkey  banana
[2] monkey  banana peanut

Kaldırılacak hatları seçtikten sonra:

removelist = [2,1]

def remove_duplicates(file, removelist):
    removelist = sorted(removelist, reverse=True)
    with open(file, "r") as sourcefile:
        data = sourcefile.readlines()
    for index in removelist:
        data.pop(index)
    with open(file, "wt") as sourcefile:
        for line in data:
            sourcefile.write(line)

remove_duplicates(file, removelist)

0

Aşağıdakilere bakın file.txt:

addons.mozilla.org/en-US/firefox/addon/click-to-play-per-element/ ::: C2P per-element
addons.mozilla.org/en-us/firefox/addon/prospector-oneLiner/ ::: OneLiner
askubuntu.com/q/21033 ::: What is the difference between gksudo and gksu?
askubuntu.com/q/21148 ::: openoffice calc sheet tabs (also askubuntu.com/q/138623)
askubuntu.com/q/50540 ::: What is Ubuntu's Definition of a "Registered Application"?
askubuntu.com/q/53762 ::: How to use lm-sensors?
askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors
stackoverflow.com/q/4594319 ::: bash - shell replace cr\lf by comma
stackoverflow.com/q/4594319 ::: shell replace cr\lf by comma
wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence
wiki.ubuntu.com/ClipboardPersistence ::: ClipboardPersistence - Ubuntu Wiki
www.youtube.com/watch?v=1olY5Qzmbk8 ::: Create new mime types in Ubuntu
www.youtube.com/watch?v=2hu9JrdSXB8 ::: Change mouse cursor
www.youtube.com/watch?v=Yxfa2fXJ1Wc ::: Mouse cursor size

Liste kısa olduğu için, (sıraladıktan sonra) üç kopya kopyası olduğunu görebiliyorum.

Sonra, örneğin, tutmayı seçebilirsiniz:

askubuntu.com/q/53762 ::: How to use lm-sensors?

ziyade

askubuntu.com/q/53762 ::: how-to-use-to-use-lm-sensors

Ancak daha uzun bir liste için bu zor olacaktır. İki düşündüren cevaplar biri dayanarak uniqve diğer düşündüren cut, ben bu komut bana istiyoruz çıktı verir bulabilirsiniz:

$ cut -d " " -f1 file.txt | uniq -d
askubuntu.com/q/53762
stackoverflow.com/q/4594319
wiki.ubuntu.com/ClipboardPersistence
$

Cevabımı başka bir varyantı ile güncelledim cut. Çoğaltmayı kaldırma işi yapıyorsanız, satır numaraları çok yardımcı olabilir. Tüm kopyaları yazdırmak için -Dyerine seçeneğini kullanın -d.
Lekensteyn

Sanırım daha iyi kullanmalısın: for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n $dup file.txt; donecevabımda olduğu gibi. İlgilendiğiniz şey hakkında size daha iyi bir önizleme verecektir.
Radu Rădeanu

0

Onu nasıl çözdüm:

file_with_duplicates:

1,a,c
2,a,d
3,a,e <--duplicate
4,a,t
5,b,k <--duplicate
6,b,l
7,b,s
8,b,j
1,b,l
3,a,d <--duplicate
5,b,l <--duplicate

Sütun 1 ve 2'ye göre sıralanan ve tekilleştirilen dosya:

sort -t',' -k1,1 -k2,2 -u file_with_duplicates

Yalnızca sütun 1 ve 2'ye göre sıralanan dosya:

sort -t',' -k1,1 -k2,2 file_with_duplicates

Yalnızca farkı göster:

diff <(sort -t',' -k1,1 -k2,2 -u file_with_duplicates) <(sort -t',' -k1,1 -k2,2 file_with_duplicates)

 3a4
   3,a,d
 6a8
   5,b,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.