Identifikasi duplikat baris dalam file tanpa menghapusnya?

11

Saya memiliki referensi saya sebagai file teks dengan daftar panjang entri dan masing-masing memiliki dua (atau lebih) bidang.

Kolom pertama adalah url referensi; kolom kedua adalah judul yang mungkin sedikit berbeda tergantung bagaimana entri dibuat. Sama untuk bidang ketiga yang mungkin ada atau tidak ada.

Saya ingin mengidentifikasi tetapi tidak menghapus entri yang memiliki bidang pertama (url referensi) yang identik. Saya tahu tentang sort -k1,1 -utetapi itu akan secara otomatis (non-interaktif) menghapus semua kecuali hit pertama. Apakah ada cara untuk memberi tahu saya agar saya dapat memilih mana yang akan dipertahankan?

Dalam ekstrak di bawah tiga baris yang memiliki bidang pertama yang sama ( http://unix.stackexchange.com/questions/49569/), saya ingin menyimpan baris 2 karena memiliki tag tambahan (urutkan, CLI) dan hapus baris # 1 dan # 3:

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

Apakah ada program untuk membantu mengidentifikasi "duplikat" seperti itu? Lalu, saya dapat membersihkan secara manual dengan menghapus baris # 1 dan # 3 secara pribadi?

DK Bose
sumber
Saya tidak begitu mengerti contoh Anda ... bisakah Anda memberikan versi yang lebih sederhana dari input dan output yang diharapkan?
Oli
Silakan lihat apakah sekarang lebih jelas?
DK Bose

Jawaban:

9

Jika saya mengerti pertanyaan Anda, saya pikir Anda perlu sesuatu seperti:

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

atau:

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

di mana file.txtfile Anda yang berisi data tentang Anda tertarik.

Dalam output Anda akan melihat jumlah garis dan garis di mana bidang pertama ditemukan dua kali atau lebih.

Radu Rădeanu
sumber
3
Terima kasih: bahkan cut -d " " -f1 file.txt | uniq -dmemberi saya hasil yang bagus.
DK Bose
@ DKBose Mungkin ada lebih banyak kemungkinan, tetapi saya ingin menggunakan dan perintah Anda juga.
Radu Rădeanu
Terima kasih. Perintah kedua adalah yang saya suka. Anda bisa menghapus yang pertama. Dan jika Anda menjelaskan kode yang akan menyenangkan juga :)
DK Bose
10

Ini adalah masalah klasik yang bisa diselesaikan dengan uniqperintah. uniqdapat mendeteksi duplikat baris berturut-turut dan menghapus duplikat ( -u, --unique) atau menyimpan duplikat saja ( -d, --repeated).

Karena memesan garis duplikat tidak penting bagi Anda, Anda harus mengurutkannya terlebih dahulu. Kemudian gunakan uniquntuk mencetak garis unik saja:

sort yourfile.txt | uniq -u

Ada juga opsi -c( --count) yang mencetak jumlah duplikat untuk -dopsi. Lihat halaman manual uniquntuk detailnya.


Jika Anda benar-benar tidak peduli tentang bagian-bagian setelah bidang pertama, Anda dapat menggunakan perintah berikut untuk menemukan kunci duplikat dan mencetak setiap nomor baris untuk itu (tambahkan yang lain | sort -nuntuk memiliki output diurutkan berdasarkan baris):

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

Karena Anda ingin melihat garis duplikat (menggunakan bidang pertama sebagai kunci), Anda tidak dapat langsung menggunakan uniq. Masalah yang membuat otomatisasi sulit adalah bahwa bagian-bagian judul bervariasi, tetapi suatu program tidak dapat secara otomatis menentukan judul mana yang harus dianggap sebagai judul akhir.

Berikut adalah skrip AWK (simpan ke script.awk) yang mengambil file teks Anda sebagai input dan mencetak semua baris duplikat sehingga Anda dapat memutuskan mana yang akan dihapus. ( 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];
            }
        }
    }
}
Lekensteyn
sumber
Saya pikir ini dekat dengan apa yang saya inginkan tetapi saya perlu kebalikan dari `-f, --skip-fields = N (hindari membandingkan bidang N pertama). Dengan kata lain, saya hanya ingin bidang pertama, url, dipertimbangkan.
DK Bose
@DKBose Ada opsi -w( --check-chars) untuk membatasi jumlah karakter tetap, tetapi melihat contoh Anda, Anda memiliki bidang variabel pertama. Karena uniqtidak mendukung pemilihan bidang, Anda harus menggunakan solusi. Saya akan menyertakan contoh AWK karena itu lebih mudah.
Lekensteyn
Ya, saya hanya melihat -wtetapi panjang bidang pertama adalah variabel :(
DK Bose
@DKBose Silakan lihat hasil edit terbaru
Lekensteyn
1
Saya mulai awk: script.awk: baris 4: kesalahan sintaks pada atau dekat [awk: script.awk: baris 10: kesalahan sintaks pada atau dekat [awk: script.awk: baris 18: kesalahan sintaks pada atau dekat}
DK Bose
2

Jika saya membaca ini dengan benar, yang Anda butuhkan adalah sesuatu seperti

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

Itu akan mencetak nomor baris yang berisi dupe dan baris itu sendiri. Misalnya, menggunakan file ini:

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

Ini akan menghasilkan output ini:

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

Untuk hanya mencetak nomor baris, Anda bisa melakukannya

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

Dan hanya untuk mencetak garis:

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

Penjelasan:

The awkScript hanya mencetak ruang 1 dipisahkan bidang file. Gunakan $Nuntuk mencetak bidang Nth. sortmengurutkannya dan uniq -cmenghitung kemunculan setiap baris.

Ini kemudian diteruskan ke whileloop yang menyimpan jumlah kemunculan sebagai $numdan garis sebagai $dupedan jika $numlebih besar dari satu (jadi itu digandakan setidaknya satu kali) itu akan mencari file untuk garis itu, gunakan -nuntuk mencetak nomor baris. The --memberitahu grepbahwa apa yang berikut adalah bukan pilihan baris perintah, berguna ketika $dupebisa mulai dengan -.

terdon
sumber
1

Tidak diragukan lagi yang paling bertele-tele dalam daftar, mungkin bisa lebih pendek:

#!/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)

berikan pada file teks seperti:

monkey  banana
dog  bone
monkey  banana peanut
cat  mice
dog  cowmeat

sebuah output seperti:

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

Setelah Anda memilih garis untuk dihapus:

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)
Yakub Vlijm
sumber
0

Lihat yang diurutkan berikut 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

Karena daftarnya pendek, saya bisa melihat (setelah memilah) bahwa ada tiga set duplikat.

Lalu, misalnya, saya dapat memilih untuk tetap:

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

daripada

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

Tetapi untuk daftar yang lebih panjang ini akan sulit. Berdasarkan pada dua jawaban yang satu menyarankan uniqdan yang lainnya menyarankan cut, saya menemukan bahwa perintah ini memberi saya output yang saya inginkan:

$ cut -d " " -f1 file.txt | uniq -d
askubuntu.com/q/53762
stackoverflow.com/q/4594319
wiki.ubuntu.com/ClipboardPersistence
$
DK Bose
sumber
Saya telah memperbarui jawaban saya dengan varian lain dari cut. Jika Anda melakukan pekerjaan menduplikasi, maka nomor baris mungkin sangat membantu. Untuk mencetak semua duplikat, gunakan -Dopsi sebagai ganti -d.
Lekensteyn
Saya pikir Anda lebih baik menggunakan: for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n $dup file.txt; doneseperti dalam jawaban saya. Ini akan memberi Anda pratinjau yang lebih baik tentang apa yang Anda minati.
Radu Rădeanu
0

Inilah cara saya menyelesaikannya:

file_with_duplikat:

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

File diurutkan dan dideduksi berdasarkan kolom 1 dan 2:

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

File hanya diurutkan berdasarkan kolom 1 dan 2:

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

Hanya tampilkan perbedaan:

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
Clint Smith
sumber