ระบุบรรทัดที่ซ้ำกันในไฟล์โดยไม่ลบออกเหรอ?


11

ฉันมีการอ้างอิงของฉันเป็นไฟล์ข้อความที่มีรายการที่มีความยาวและแต่ละคนมีสองฟิลด์ (หรือมากกว่า)

คอลัมน์แรกคือ URL ของการอ้างอิง คอลัมน์ที่สองคือชื่อซึ่งอาจแตกต่างกันเล็กน้อยขึ้นอยู่กับวิธีการทำรายการ เหมือนกันสำหรับฟิลด์ที่สามซึ่งอาจจะมีหรือไม่มีก็ได้

ฉันต้องการระบุ แต่ไม่ลบรายการที่มีเขตข้อมูลแรก (URL อ้างอิง) เหมือนกัน ฉันรู้sort -k1,1 -uแต่จะลบออกทั้งหมดโดยอัตโนมัติ (ไม่ใช่แบบโต้ตอบ) แต่เป็น Hit แรก มีวิธีแจ้งให้ฉันทราบหรือไม่เพื่อให้ฉันสามารถเลือกได้ว่าจะเก็บรักษาไว้หรือไม่

ในสารสกัดด้านล่างของสามบรรทัดที่มีเขตข้อมูลแรกเหมือนกัน ( http://unix.stackexchange.com/questions/49569/) ฉันต้องการเก็บบรรทัด 2 ไว้เนื่องจากมีแท็กเพิ่มเติม (เรียงลำดับ CLI) และลบบรรทัด # 1 และ # 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

มีโปรแกรมที่ช่วยระบุ "รายการที่ซ้ำกัน" ดังกล่าวหรือไม่? จากนั้นฉันสามารถล้างข้อมูลด้วยตนเองโดยการลบบรรทัด # 1 และ # 3 เป็นการส่วนตัวหรือไม่


ฉันไม่เข้าใจตัวอย่างของคุณมากนัก ... คุณช่วยให้อินพุทและเวอร์ชันที่คาดหวังง่ายขึ้นได้ไหม?
Oli

โปรดดูว่าชัดเจนขึ้นหรือไม่?
DK Bose

คำตอบ:


9

หากฉันเข้าใจคำถามของคุณฉันคิดว่าคุณต้องการอะไร:

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

หรือ:

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

ที่file.txtเป็นไฟล์ของคุณที่มีข้อมูลเกี่ยวกับคุณมีความสนใจ

ในผลลัพธ์คุณจะเห็นจำนวนบรรทัดและบรรทัดที่พบฟิลด์แรกสองครั้งขึ้นไป


3
ขอบคุณ: แม้ cut -d " " -f1 file.txt | uniq -dให้ผลลัพธ์ที่ดี
DK Bose

@DKBose อาจมีความเป็นไปได้มากกว่านี้ แต่ฉันต้องการใช้และคำสั่งของคุณด้วย
Radu Rădeanu

ขอบคุณ คำสั่งที่สองคือคำสั่งที่ฉันชอบ คุณสามารถลบออกก่อน และถ้าคุณอธิบายรหัสที่จะดียัง :)
DK Bose

10

นี่เป็นปัญหาคลาสสิกที่สามารถแก้ไขได้ด้วยuniqคำสั่ง uniqสามารถตรวจจับบรรทัดที่ซ้ำกันตามลำดับและลบรายการที่ซ้ำกัน ( -u, --unique) หรือเก็บรายการที่ซ้ำกันเท่านั้น ( -d, --repeated)

เนื่องจากการเรียงลำดับของบรรทัดที่ซ้ำกันนั้นไม่มีความสำคัญสำหรับคุณคุณควรเรียงลำดับก่อน จากนั้นใช้uniqเพื่อพิมพ์บรรทัดที่ไม่ซ้ำกันเท่านั้น:

sort yourfile.txt | uniq -u

นอกจากนี้ยังมีตัวเลือก-c( --count) ที่พิมพ์จำนวนซ้ำสำหรับ-dตัวเลือก ดูหน้าคู่มือของuniqสำหรับรายละเอียด


หากคุณไม่สนใจชิ้นส่วนหลังจากฟิลด์แรกคุณสามารถใช้คำสั่งต่อไปนี้เพื่อค้นหาคีย์ที่ซ้ำกันและพิมพ์หมายเลขบรรทัดแต่ละหมายเลขสำหรับมัน (ผนวกส่วนอื่น| sort -nเพื่อให้ได้ผลลัพธ์เรียงตามบรรทัด):

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

เนื่องจากคุณต้องการที่จะเห็นเส้นที่ซ้ำกัน (โดยใช้ฟิลด์แรกเป็นคีย์) uniqคุณไม่สามารถใช้โดยตรง ปัญหาที่ทำให้ระบบอัตโนมัติยากคือส่วนต่าง ๆ ของชื่อแตกต่างกันไป แต่โปรแกรมไม่สามารถกำหนดได้โดยอัตโนมัติว่าชื่อเรื่องใดควรพิจารณาเป็นชื่อสุดท้าย

นี่คือสคริปต์ AWK (บันทึกไปที่script.awk) ซึ่งใช้ไฟล์ข้อความของคุณเป็นอินพุตและพิมพ์บรรทัดที่ซ้ำกันทั้งหมดเพื่อให้คุณสามารถตัดสินใจว่าจะลบไฟล์ใด ( 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];
            }
        }
    }
}

ฉันคิดว่านี่ใกล้กับสิ่งที่ฉันต้องการ แต่ฉันต้องการตรงกันข้ามกับ `-f, --skip-fields = N (หลีกเลี่ยงการเปรียบเทียบฟิลด์ N แรก) กล่าวอีกนัยหนึ่งฉันต้องการเฉพาะเขตข้อมูลแรก URL ที่จะพิจารณา
DK Bose

@DKBose มีตัวเลือก-w( --check-chars) เพื่อ จำกัด จำนวนอักขระที่แน่นอน แต่เมื่อเห็นตัวอย่างของคุณคุณจะมีเขตข้อมูลตัวแปรแรก เนื่องจากuniqไม่รองรับการเลือกฟิลด์คุณจึงต้องใช้วิธีแก้ปัญหา ฉันจะรวมตัวอย่าง AWK เนื่องจากง่ายกว่า
Lekensteyn

ใช่ฉันเพิ่งดู-wแต่ความยาวของฟิลด์แรกคือตัวแปร :(
DK Bose

@DKBose โปรดดูการแก้ไขล่าสุด
Lekensteyn

1
ฉันได้รับ awk: script.awk: บรรทัด 4: ข้อผิดพลาดทางไวยากรณ์ที่หรือใกล้ [awk: script.awk: บรรทัด 10: ข้อผิดพลาดทางไวยากรณ์ที่หรือใกล้ [awk: script.awk: บรรทัด 18: ข้อผิดพลาดทางไวยากรณ์ที่หรือใกล้}
DK Bose

2

ถ้าฉันอ่านมันถูกต้องสิ่งที่คุณต้องการก็คือ

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

ที่จะพิมพ์จำนวนบรรทัดที่มี dupe และ line เอง ตัวอย่างเช่นการใช้ไฟล์นี้:

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

มันจะสร้างผลลัพธ์นี้:

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

หากต้องการพิมพ์เฉพาะจำนวนบรรทัดคุณสามารถทำได้

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

และเพื่อพิมพ์เฉพาะบรรทัด:

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

คำอธิบาย:

awkสคริปต์เพียงแค่พิมพ์พื้นที่ 1 ฟิลด์แยกของไฟล์ ใช้$Nเพื่อพิมพ์ฟิลด์ Nth sortเรียงลำดับและuniq -cนับจำนวนการเกิดของแต่ละบรรทัด

สิ่งนี้จะถูกส่งผ่านไปยังwhileลูปซึ่งจะบันทึกจำนวนการเกิดเป็น$numและบรรทัดเป็น$dupeและถ้า$numมากกว่าหนึ่ง (ดังนั้นมันจะทำซ้ำอย่างน้อยหนึ่งครั้ง) มันจะค้นหาไฟล์สำหรับบรรทัดนั้นโดยใช้-nเพื่อพิมพ์หมายเลขบรรทัด --บอกgrepว่าสิ่งที่ตามมาคือไม่ได้เป็นตัวเลือกบรรทัดคำสั่งที่มีประโยชน์สำหรับเมื่อสามารถเริ่มต้นด้วย$dupe-


1

ไม่ต้องสงสัยเลยว่า verbose มากที่สุดในรายการอาจจะสั้นกว่า:

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

ให้ใน textfile เช่น:

monkey  banana
dog  bone
monkey  banana peanut
cat  mice
dog  cowmeat

ผลลัพธ์เช่น:

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

เมื่อคุณเลือกบรรทัดเพื่อลบ:

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

ดูการเรียงลำดับต่อไปนี้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

เนื่องจากรายการสั้นฉันสามารถเห็น (หลังการเรียงลำดับ) ว่ามีการทำซ้ำสามชุด

ยกตัวอย่างเช่นฉันสามารถเลือกที่จะเก็บ:

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

ค่อนข้างมากกว่า

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

แต่สำหรับรายการที่ยาวกว่านี้จะเป็นเรื่องยาก จากคำตอบสองข้อที่ผู้แนะนำคนหนึ่งuniqและผู้แนะนำคนอื่น ๆcutพบว่าคำสั่งนี้ให้ผลลัพธ์ที่ฉันต้องการ:

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

cutฉันมีการปรับปรุงคำตอบของฉันมีตัวแปรของผู้อื่น หากคุณทำงานซ้ำซ้อนหมายเลขบรรทัดอาจมีประโยชน์มาก หากต้องการพิมพ์ซ้ำกันทั้งหมดใช้ตัวเลือกแทน-D -d
Lekensteyn

ฉันคิดว่าคุณควรใช้: for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n $dup file.txt; doneเหมือนคำตอบของฉัน มันจะให้ภาพตัวอย่างที่ดีขึ้นเกี่ยวกับสิ่งที่คุณสนใจ
Radu Rădeanu

0

เธอเป็นวิธีที่ฉันแก้ไขมัน:

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

ไฟล์ที่เรียงลำดับและลบข้อมูลโดยคอลัมน์ 1 และ 2:

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

ไฟล์เรียงตามคอลัมน์ 1 และ 2:

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

แสดงความแตกต่างเท่านั้น:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.