Xác định các dòng trùng lặp trong một tập tin mà không xóa chúng?


11

Tôi có tài liệu tham khảo của mình dưới dạng tệp văn bản với một danh sách dài các mục và mỗi mục có hai (hoặc nhiều) trường.

Cột đầu tiên là url của tài liệu tham khảo; cột thứ hai là tiêu đề có thể thay đổi một chút tùy thuộc vào cách thực hiện mục nhập. Tương tự cho trường thứ ba có thể có hoặc không có mặt.

Tôi muốn xác định nhưng không xóa các mục có trường đầu tiên (url tham chiếu) giống hệt nhau. Tôi biết sort -k1,1 -unhưng điều đó sẽ tự động (không tương tác) loại bỏ tất cả trừ lần truy cập đầu tiên. Có cách nào để chỉ cho tôi biết để tôi có thể chọn giữ lại không?

Trong trích xuất bên dưới của ba dòng có cùng trường đầu tiên ( http://unix.stackexchange.com/questions/49569/), tôi muốn giữ dòng 2 vì nó có các thẻ bổ sung (sort, CLI) và xóa các dòng # 1 và # 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

Có chương trình nào giúp xác định những "bản sao" như vậy không? Sau đó, tôi có thể tự dọn dẹp bằng cách xóa cá nhân các dòng # 1 và # 3 không?


Tôi hoàn toàn không hiểu ví dụ của bạn ... bạn có thể đưa ra một phiên bản đơn giản hơn của đầu vào và đầu ra dự kiến ​​không?
Oli

Xin vui lòng xem nếu nó rõ ràng hơn bây giờ?
DK Bose

Câu trả lời:


9

Nếu tôi hiểu câu hỏi của bạn, tôi nghĩ rằng bạn cần một cái gì đó như:

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

hoặc là:

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

nơi file.txtlà tập tin của bạn chứa dữ liệu về bạn quan tâm.

Trong đầu ra, bạn sẽ thấy số lượng dòng và dòng nơi trường đầu tiên được tìm thấy hai lần trở lên.


3
Cảm ơn bạn: thậm chí cut -d " " -f1 file.txt | uniq -dcho tôi đầu ra tốt đẹp.
DK Bose

@DKBose Có lẽ có nhiều khả năng hơn, nhưng tôi muốn sử dụng và lệnh của bạn cũng vậy.
Radu Rădeanu

Cảm ơn. Lệnh thứ hai là lệnh tôi thích. Bạn có thể loại bỏ đầu tiên. Và nếu bạn giải thích mã đó cũng sẽ tốt đẹp :)
DK Bose

10

Đây là một vấn đề cổ điển có thể được giải quyết bằng uniqlệnh. uniqcó thể phát hiện trùng lặp liên tiếp dòng và bản sao remove ( -u, --unique) hoặc chỉ giữ bản sao ( -d, --repeated).

Vì việc đặt hàng các dòng trùng lặp không quan trọng đối với bạn, bạn nên sắp xếp nó trước. Sau đó, chỉ sử dụng uniqđể in các dòng duy nhất:

sort yourfile.txt | uniq -u

Ngoài ra còn có một tùy chọn -c( --count) in số lượng trùng lặp cho -dtùy chọn. Xem trang hướng dẫn uniqđể biết chi tiết.


Nếu bạn thực sự không quan tâm đến các phần sau trường đầu tiên, bạn có thể sử dụng lệnh sau để tìm các khóa trùng lặp và in từng số dòng cho nó (nối thêm một số khác | sort -nđể có đầu ra được sắp xếp theo dòng):

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

Vì bạn muốn xem các dòng trùng lặp (sử dụng trường đầu tiên làm khóa), bạn không thể trực tiếp sử dụng uniq. Vấn đề làm cho tự động hóa trở nên khó khăn là các phần tiêu đề khác nhau, nhưng một chương trình không thể tự động xác định tiêu đề nào nên được coi là tiêu đề cuối cùng.

Dưới đây là tập lệnh AWK (lưu nó vào script.awk) lấy tệp văn bản của bạn làm đầu vào và in tất cả các dòng trùng lặp để bạn có thể quyết định xóa. ( 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];
            }
        }
    }
}

Tôi nghĩ rằng nó gần với những gì tôi muốn nhưng tôi cần ngược lại với `-f, --skip-Field = N (tránh so sánh các trường N đầu tiên). Nói cách khác, tôi chỉ muốn trường đầu tiên, các url, được xem xét.
DK Bose

@DKBose Có một tùy chọn -w( --check-chars) để giới hạn số lượng ký tự cố định, nhưng xem ví dụ của bạn, bạn có các trường đầu tiên thay đổi. Vì uniqkhông hỗ trợ lựa chọn trường, bạn phải sử dụng một cách giải quyết. Tôi sẽ bao gồm một ví dụ AWK vì điều đó dễ dàng hơn.
Lekensteyn

Có, tôi chỉ nhìn vào -wnhưng độ dài của trường đầu tiên có thể thay đổi :(
DK Bose

@DKBose Vui lòng xem bản chỉnh sửa mới nhất
Lekensteyn

1
Tôi đang nhận awk: script.awk: dòng 4: lỗi cú pháp tại hoặc gần [awk: script.awk: dòng 10: lỗi cú pháp tại hoặc gần [awk: script.awk: dòng 18: lỗi cú pháp tại hoặc gần}
DK Bose

2

Nếu tôi đọc chính xác, tất cả những gì bạn cần là một cái gì đó như

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

Điều đó sẽ in ra số dòng chứa bản sao và chính dòng đó. Ví dụ: sử dụng tệp này:

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

Nó sẽ tạo ra đầu ra này:

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

Để chỉ in số dòng, bạn có thể làm

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

Và chỉ in dòng:

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

Giải trình:

Các awkkịch bản chỉ in không gian 1 tách lĩnh vực của tập tin. Sử dụng $Nđể in trường thứ N. sortsắp xếp nó và uniq -cđếm số lần xuất hiện của mỗi dòng.

Điều này sau đó được chuyển đến whilevòng lặp lưu số lần xuất hiện $numvà dòng như $dupevà nếu $numlớn hơn một (vì vậy nó được nhân đôi ít nhất một lần), nó sẽ tìm kiếm tệp cho dòng đó, sử dụng -nđể in số dòng. Thông báo --cho grepbiết những gì tiếp theo không phải là một tùy chọn dòng lệnh, hữu ích khi $dupecó thể bắt đầu bằng -.


1

Không nghi ngờ gì một trong những dài dòng nhất trong danh sách, có thể có thể ngắn hơn:

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

đưa ra một tệp văn bản như:

monkey  banana
dog  bone
monkey  banana peanut
cat  mice
dog  cowmeat

một đầu ra như:

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

Khi bạn chọn các dòng để loại bỏ:

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

Xem các sắp xếp sau đây 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

Bởi vì danh sách này ngắn, tôi có thể thấy (sau khi sắp xếp) có ba bộ trùng lặp.

Sau đó, ví dụ, tôi có thể chọn giữ:

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

thay vì

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

Nhưng đối với một danh sách dài hơn, điều này sẽ khó khăn. Dựa trên hai câu trả lời một gợi ý uniqvà gợi ý khác cut, tôi thấy rằng lệnh này mang lại cho tôi đầu ra mà tôi muốn:

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

Tôi đã cập nhật câu trả lời của tôi với một biến thể khác của cut. Nếu bạn đang thực hiện công việc khử trùng lặp, thì số dòng có thể rất hữu ích. Để in tất cả các bản sao, sử dụng -Dtùy chọn thay vì -d.
Lekensteyn

Tôi nghĩ bạn sử dụng tốt hơn: for dup in $(cut -d " " -f1 file.txt | uniq -d); do grep -n $dup file.txt; donenhư trong câu trả lời của tôi. Nó sẽ cung cấp cho bạn một bản xem trước tốt hơn về những gì bạn quan tâm.
Radu Rădeanu

0

Cô ấy là cách tôi giải quyết nó:

file_with_d repeatates:

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

Tệp được sắp xếp và khấu trừ theo cột 1 và 2:

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

Tệp chỉ được sắp xếp theo cột 1 và 2:

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

Chỉ hiển thị sự khác biệt:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.