So sánh hai tệp trong thiết bị đầu cuối linux


168

Có hai tệp được gọi là "a.txt""b.txt" đều có danh sách các từ. Bây giờ tôi muốn kiểm tra những từ nào là thêm trong "a.txt" và không có trong "b.txt" .

Tôi cần một thuật toán hiệu quả vì tôi cần so sánh hai từ điển.


27
diff a.txt b.txtKhông đủ?
ThanksFor ALLTheFish

Các từ có thể xảy ra nhiều lần trong mỗi tập tin? Bạn có thể sắp xếp các tập tin?
Basile Starynkevitch

tôi chỉ cần những từ không có trong "b.txt" và có trong a.txt
Ali Imran

Câu trả lời:


343

Nếu bạn đã cài đặt vim, hãy thử điều này:

vimdiff file1 file2

hoặc là

vim -d file1 file2

bạn sẽ thấy nó thật tuyệt vờinhập mô tả hình ảnh ở đây


9
chắc chắn tuyệt vời, tốt trong thiết kế và dễ dàng để tìm ra sự khác biệt. Ohmygod
Zen

1
Câu trả lời của bạn thật tuyệt vời, nhưng giáo viên của tôi yêu cầu tôi không sử dụng bất kỳ chức năng thư viện nào: P
Ali Imran

1
Thật là một công cụ tuyệt vời! Điều này là vô cùng hữu ích.
dùng1205577

1
Ý nghĩa của những màu sắc đó là gì?
zygimantus

1
Các mã màu có nghĩa là chúng khác nhau trong hai tập tin. @zygimantus
Fengya Li

73

Sắp xếp chúng và sử dụng comm:

comm -23 <(sort a.txt) <(sort b.txt)

commso sánh (sắp xếp) các tệp đầu vào và theo mặc định xuất ra ba cột: các dòng duy nhất cho a, các dòng duy nhất cho b và các dòng có trong cả hai. Bằng cách xác định -1, -2và / hoặc -3bạn có thể ngăn chặn các đầu ra tương ứng. Do đó, comm -23 a bchỉ liệt kê các mục duy nhất cho a. Tôi sử dụng <(...)cú pháp để sắp xếp các tệp một cách nhanh chóng, nếu chúng đã được sắp xếp, bạn không cần điều này.


Tôi đã thêm câu trả lời của riêng mình chỉ bằng các lệnh grep, xin vui lòng cho tôi biết nó có hiệu quả hơn không?
Ali Imran

3
@AliImran, commhiệu quả hơn vì nó thực hiện công việc trong một lần chạy, mà không lưu trữ toàn bộ tệp trong bộ nhớ. Vì bạn đang sử dụng từ điển rất có thể đã được sắp xếp, bạn thậm chí không cần đến sortchúng. grep -f file1 file2Mặt khác, việc sử dụng sẽ tải toàn bộ file1vào bộ nhớ và so sánh từng dòng file2với tất cả các mục đó, sẽ kém hiệu quả hơn nhiều. Nó chủ yếu hữu ích cho nhỏ, chưa được sắp xếp -f file1.
Anders Johansson

1
Cảm ơn @AndersJohansson vì đã chia sẻ lệnh "comm". Nó thực sự tiện lợi. Tôi thường xuyên phải thực hiện các phép nối ngoài giữa các tệp và điều này thực hiện thủ thuật.
blispr

Hãy chú ý đến nhân vật dòng mới ... Tôi chỉ thấy rằng \nnó cũng sẽ được đưa vào để so sánh.
Bin

31

Hãy thử sdiff( man sdiff)

sdiff -s file1 file2

28

Bạn có thể sử dụng diffcông cụ trong linux để so sánh hai tập tin. Bạn có thể sử dụng các tùy chọn định dạng --changed-group-format--unchanged-group để lọc dữ liệu cần thiết.

Có thể sử dụng ba tùy chọn sau để chọn nhóm phù hợp cho từng tùy chọn:

  • '% <' nhận các dòng từ FILE1

  • '%>' nhận các dòng từ FILE2

  • '' (Chuỗi trống) để xóa các dòng từ cả hai tệp.

Ví dụ: diff --changed-group-format = "% <" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

Nếu bạn thích kiểu đầu ra khác từ git diff, bạn có thể sử dụng nó với --no-indexcờ để so sánh các tệp không có trong kho git:

git diff --no-index a.txt b.txt

Sử dụng một vài tệp với mỗi chuỗi khoảng 200k tên tệp, tôi đã điểm chuẩn (với timelệnh tích hợp) phương pháp này so với một số câu trả lời khác ở đây:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commdường như là nhanh nhất cho đến nay, trong khi git diff --no-indexdường như là cách tiếp cận nhanh nhất cho đầu ra kiểu khác.


Cập nhật 2018-03-25 Bạn thực sự có thể bỏ qua --no-indexcờ trừ khi bạn ở trong kho git và muốn so sánh các tệp không bị theo dõi trong kho đó. Từ trang người đàn ông :

Hình thức này là để so sánh hai đường dẫn đã cho trên hệ thống tập tin. Bạn có thể bỏ qua tùy chọn --no-index khi chạy lệnh trong cây làm việc được điều khiển bởi Git và ít nhất một trong các đường dẫn bên ngoài cây làm việc hoặc khi chạy lệnh bên ngoài cây làm việc do Git điều khiển.




4

Sử dụng comm -13 (yêu cầu các tệp được sắp xếp) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

Đây là giải pháp của tôi cho việc này:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
Bạn đã thử bất kỳ giải pháp khác? Có phải một trong những giải pháp này hữu ích cho bạn? Câu hỏi của bạn đủ chung chung để thu hút nhiều người dùng, nhưng câu trả lời của bạn cụ thể hơn theo sở thích của tôi ... Đối với trường hợp cụ thể của tôi sdiff -s file1 file2là hữu ích.
Metafaniel

@Metafaniel giải pháp của tôi không sử dụng lệnh sdiff. Nó chỉ sử dụng linux được xây dựng trong các lệnh để giải quyết vấn đề.
Ali Imran

-1

Sử dụng awk cho nó. Hồ sơ kiểm tra:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

Awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

Các bản sao được xuất ra:

four
four

Để tránh trùng lặp, hãy thêm từng từ mới gặp trong a.txt vào seenhàm băm:

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

Đầu ra:

four

Nếu danh sách từ được phân tách bằng dấu phẩy, như:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

bạn phải thực hiện thêm một vài vòng ( forvòng lặp):

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

Đầu ra lần này:

four
five,six
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.