So sánh hai tệp từng dòng và tạo ra sự khác biệt trong tệp khác


121

Tôi muốn so sánh tệp1 với tệp2 và tạo tệp 3 chứa các dòng trong tệp1 không có trong tệp2.


Tôi đã thử diff nhưng nó tạo ra một số số và các ký hiệu khác ở phía trước các dòng khác nhau khiến tôi khó so sánh các tệp.
nhật

Câu trả lời:


216

diff (1) không phải là câu trả lời, nhưng comm (1) là.

NAME
       comm - compare two sorted files line by line

SYNOPSIS
       comm [OPTION]... FILE1 FILE2

...

       -1     suppress lines unique to FILE1

       -2     suppress lines unique to FILE2

       -3     suppress lines that appear in both files

Vì thế

comm -2 -3 file1 file2 > file3

Các tệp đầu vào phải được sắp xếp. Nếu không, hãy sắp xếp chúng trước. Điều này có thể được thực hiện bằng một tệp tạm thời hoặc ...

comm -2 -3 <(sort file1) <(sort file2) > file3

miễn là shell của bạn hỗ trợ thay thế quy trình (bash thì có).


1
Hãy nhớ rằng hai tệp phải được sắp xếp và là duy nhất
andy

6
Bạn có thể nhóm các tùy chọn lại với nhau:comm -23
Paolo M

"Đã sắp xếp" có nghĩa là gì? Rằng các dòng có cùng một thứ tự? Sau đó, nó có thể tốt cho hầu hết các trường hợp sử dụng - như trong, kiểm tra những dòng nào đã được thêm vào bằng cách so sánh với phiên bản cũ hơn được sao lưu. Nếu các dòng mới được thêm vào không thể nằm giữa các dòng hiện có, đó là một vấn đề lớn hơn.
Egor Hans

@EgorHans: nếu tệp có các dòng ví dụ như chứa số nguyên, chẳng hạn như "3 \ n1 \ n3 \ n2 \ n", trước tiên các dòng phải được sắp xếp lại theo thứ tự tăng dần hoặc giảm dần, ví dụ: "\ 1 \ n2 \ n3 \ n3 \ n" với các bản sao liền kề. Đó là "được sắp xếp" và cả hai tệp phải được sắp xếp theo cách tương tự. Khi tệp mới hơn có các dòng mới, không quan trọng nếu chúng nằm "giữa các dòng hiện có" bởi vì sau khi sắp xếp, chúng không nằm trong thứ tự được sắp xếp.
sorpigal

48

Tiện ích Unix diffcó nghĩa là cho chính xác mục đích này.

$ diff -u file1 file2 > file3

Xem sách hướng dẫn và Internet để biết các tùy chọn, các định dạng đầu ra khác nhau, v.v.


8
Điều đó không làm công việc được yêu cầu; nó chèn một loạt các ký tự bổ sung, ngay cả khi sử dụng các công tắc dòng lệnh được đề xuất trong các câu trả lời khác.
xenocyon

20

Hãy xem xét điều này:
tệp a.txt:

abcd
efgh

tệp b.txt:

abcd

Bạn có thể tìm thấy sự khác biệt với:

diff -a --suppress-common-lines -y a.txt b.txt

Đầu ra sẽ là:

efgh 

Bạn có thể giới hạn lại đầu ra trong tệp đầu ra (c.txt) bằng cách sử dụng:

diff -a --suppress-common-lines -y a.txt b.txt > c.txt

Điều này sẽ trả lời câu hỏi của bạn:

"... chứa các dòng trong file1 không có trong file2."


2
Có hai hạn chế đối với câu trả lời này: (1) nó chỉ hoạt động với các dòng ngắn (ít hơn 80 ký tự theo mặc định, mặc dù điều này có thể được sửa đổi) và quan trọng hơn, (2) nó thêm dấu "<" vào cuối mỗi ký tự dòng phải được lấy đi bằng chương trình khác (ví dụ: awk, sed).
sergut

Trong nhiều trường hợp, bạn cũng sẽ muốn sử dụng -d, điều này sẽ cố gắng diffhết sức để tìm ra sự khác biệt nhỏ nhất có thể. -i, -E, -w, -B--suppress-blank-emptycũng có thể hữu ích đôi khi, mặc dù không phải lúc nào. Nếu bạn không biết điều gì phù hợp với trường hợp sử dụng của mình, hãy thử diff --helptrước (thường là một ý kiến ​​hay khi bạn không biết lệnh có thể làm gì).
Egor Hans

Ngoài ra, bằng cách sử dụng --line-format =% L, bạn sẽ không tạo ra bất kỳ ký tự thừa nào (ít nhất, trợ giúp cho biết nó hoạt động như thế này, nhưng bạn sắp dùng thử).
Egor Hans

Ngoài ra đây là ngắn hơn và dường như hoạt động giống stackoverflow.com/a/27667185/1179925
mrgloom

8

Đôi khi difflà tiện ích bạn cần, nhưng đôi khi joinlại thích hợp hơn. Các tệp cần được sắp xếp trước hoặc, nếu bạn đang sử dụng trình bao hỗ trợ thay thế quy trình như bash, ksh hoặc zsh, bạn có thể thực hiện sắp xếp nhanh chóng.

join -v 1 <(sort file1) <(sort file2)

Bạn sẽ nhận được một huy chương cho điều này! Đó chính xác là những gì tôi đã tìm kiếm trong 2 giờ qua
Zatarra

7

Thử

sdiff file1 file2

Nó thường hoạt động tốt hơn nhiều trong hầu hết các trường hợp đối với tôi. Bạn có thể muốn sắp xếp các tệp trước, nếu thứ tự các dòng không quan trọng (ví dụ: một số tệp cấu hình văn bản).

Ví dụ,

sdiff -w 185 file1.cfg file2.cfg

1
Tiện ích tốt đẹp! Tôi thích cách nó đánh dấu các đường phân biệt. Giúp việc so sánh các cấu hình dễ dàng hơn nhiều. Cùng này với loại là một sự kết hợp chết người (ví dụ sdiff <(sort file1) <(sort file2))
jmagnusson

3

Nếu bạn cần giải quyết vấn đề này bằng coreutils, câu trả lời được chấp nhận là tốt:

comm -23 <(sort file1) <(sort file2) > file3

Bạn cũng có thể sử dụng sd (khác dòng), không yêu cầu sắp xếp cũng như xử lý thay thế và hỗ trợ các luồng vô hạn, như vậy:

cat file1 | sd 'cat file2' > file3

Có lẽ không có nhiều lợi ích về ví dụ này, nhưng vẫn xem xét nó; trong một số trường hợp, bạn sẽ không thể sử dụng commcũng grep -Fnhư không diff.

Đây là một bài đăng trên blog tôi đã viết về các luồng khác nhau trên thiết bị đầu cuối, giới thiệu sd.


3

Tuy nhiên, không có grepgiải pháp?

  • các dòng chỉ tồn tại trong file2:

    grep -Fxvf file1 file2 > file3
  • các dòng chỉ tồn tại trong file1:

    grep -Fxvf file2 file1 > file3
  • các dòng tồn tại trong cả hai tệp:

    grep -Fxf file1 file2 > file3

2

Nhiều câu trả lời đã có, nhưng không có câu trả lời nào trong số đó là IMHO hoàn hảo. Câu trả lời của Thanatos để lại một số ký tự thừa trên mỗi dòng và câu trả lời của Sorpigal yêu cầu các tệp phải được sắp xếp hoặc sắp xếp trước, điều này có thể không đầy đủ trong mọi trường hợp.

Tôi nghĩ rằng cách tốt nhất để nhận được các dòng có (không chars thêm, không tái đặt hàng) khác nhau và không có gì khác là sự kết hợp của diff, grepawk(hoặc tương đương).

Nếu các dòng không chứa bất kỳ "<" nào, thì một dòng lót ngắn có thể là:

diff urls.txt* | grep "<" | sed 's/< //g'

nhưng điều đó sẽ loại bỏ mọi trường hợp của "<" (nhỏ hơn, khoảng trắng) khỏi các dòng, điều này không phải lúc nào cũng OK (ví dụ: mã nguồn). Tùy chọn an toàn nhất là sử dụng awk:

diff urls.txt* | grep "<" | awk '{for (i=2; i<NF; i++) printf $i " "; print $NF}'

Một lớp lót này khác biệt cả hai tệp, sau đó lọc ra đầu ra kiểu ed của khác biệt, sau đó loại bỏ dấu "<" mà khác biệt thêm vào. Điều này hoạt động ngay cả khi các dòng chứa một số "<".


1
comm không yêu cầu sắp xếp (trong các phiên bản mới hơn?) - chỉ cần sử dụng --nocheck-order. Tôi sử dụng điều này rất nhiều khi thao tác csv từ CLI
ak5

2

Tôi ngạc nhiên là không ai đề cập diff -yđến việc tạo ra đầu ra song song , ví dụ:

diff -y file1 file2 > file3

Và trong file3(các dòng khác nhau có biểu tượng |ở giữa):

same     same
diff_1 | diff_2

1

Sử dụng tiện ích Diff và chỉ trích xuất các dòng bắt đầu bằng <trong đầu ra


0
diff a1.txt a2.txt | grep '> ' | sed 's/> //' > a3.txt

Tôi đã thử gần như tất cả các câu trả lời trong chủ đề này, nhưng không có câu nào hoàn thành. Sau một vài con đường mòn ở trên một đã làm việc cho tôi. diff sẽ cung cấp cho bạn sự khác biệt nhưng với một số charas đặc biệt không mong muốn. trong đó các dòng khác biệt thực tế của bạn bắt đầu bằng '>'. nên bước tiếp theo là grep dòng bắt đầu với '>' và sau đó bằng cách loại bỏ cùng với sed .


1
Đây là một ý tưởng tồi. Bạn cũng sẽ cần phải sửa đổi các dòng bắt đầu bằng <. Bạn sẽ thấy điều này nếu bạn hoán đổi thứ tự của các tệp đầu vào. Ngay cả khi bạn đã làm điều này, bạn sẽ muốn bỏ qua grepbằng cách sử dụng thêm sed: `diff a1 a2 | sed '/> / s ///' 'Điều này vẫn có thể ngắt các dòng có chứa >hoặc <trong tình huống phù hợp và vẫn để lại các dòng phụ mô tả số dòng. Nếu bạn muốn thử phương pháp này một cách tốt hơn sẽ là: diff -C0 a1 a2 | sed -ne '/^[+-] /s/^..//p'.
sorpigal

0

Bạn có thể sử dụng diffvới định dạng đầu ra sau:

diff --old-line-format='' --unchanged-line-format='' file1 file2

--old-line-format='', vô hiệu hóa đầu ra cho file1 nếu dòng so sánh khác nhau trong file2.
--unchanged-line-format='', vô hiệu hóa đầu ra nếu các dòng giống nhau.

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.