Bạn có thể đạt được điều này bằng cách kiểm soát định dạng của các dòng cũ / mới / không thay đổi trong diff
đầu ra GNU :
diff --new-line-format="" --unchanged-line-format="" file1 file2
Các tập tin đầu vào nên được sắp xếp để làm việc này. Với bash
(và zsh
) bạn có thể sắp xếp tại chỗ với quy trình thay thế <( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
Trong các dòng mới và không thay đổi ở trên bị triệt tiêu, do đó chỉ thay đổi (tức là các dòng bị loại bỏ trong trường hợp của bạn) là đầu ra. Bạn cũng có thể sử dụng một vài diff
tùy chọn mà các giải pháp khác không cung cấp, chẳng hạn như -i
bỏ qua trường hợp, hoặc tùy chọn khoảng trắng khác nhau ( -E
, -b
, -v
vv) cho phù hợp ít nghiêm ngặt.
Giải trình
Các tùy chọn --new-line-format
, --old-line-format
và --unchanged-line-format
cho phép bạn kiểm soát cách các diff
định dạng khác nhau, tương tự như printf
định dạng specifiers. Các tùy chọn này định dạng các dòng mới (đã thêm), cũ (đã xóa) và không thay đổi . Đặt một thành trống "" sẽ ngăn đầu ra của loại đường đó.
Nếu bạn quen thuộc với định dạng diff hợp nhất , bạn có thể tạo lại một phần với:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
Công %L
cụ xác định là dòng đang được đề cập và chúng tôi đặt tiền tố cho mỗi dấu "+" "-" hoặc "" diff -u
(lưu ý rằng nó chỉ tạo ra sự khác biệt, nó thiếu ---
+++
và @@
các dòng ở đầu mỗi thay đổi được nhóm). Bạn cũng có thể sử dụng để làm những việc hữu ích khác như số lượng mỗi dòng với %dn
.
Các diff
phương pháp (cùng với các đề xuất khác comm
và join
) chỉ sản xuất các đầu ra mong đợi với sắp xếp đầu vào, mặc dù bạn có thể sử dụng <(sort ...)
để sắp xếp tại chỗ. Đây là một awk
tập lệnh đơn giản (nawk) (lấy cảm hứng từ các tập lệnh được liên kết đến trong câu trả lời của Konsolebox) chấp nhận các tệp đầu vào được đặt hàng tùy ý và xuất ra các dòng bị thiếu theo thứ tự chúng xảy ra trong tệp1.
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
Điều này lưu trữ toàn bộ nội dung của dòng file1 theo dòng trong một mảng được lập chỉ mục số dòng ll1[]
và toàn bộ nội dung của dòng2 theo từng dòng trong một mảng kết hợp được lập chỉ mục nội dung dòng ss2[]
. Sau khi cả hai tệp được đọc, lặp đi lặp lại ll1
và sử dụng in
toán tử để xác định xem dòng trong tệp1 có trong tệp2 không. (Điều này sẽ có đầu ra khác với diff
phương thức nếu có trùng lặp.)
Trong trường hợp các tệp đủ lớn để lưu trữ cả hai đều gây ra vấn đề về bộ nhớ, bạn có thể trao đổi CPU cho bộ nhớ bằng cách chỉ lưu trữ tệp1 và xóa các kết quả trùng khớp trên đường đi khi tệp 2 được đọc.
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
Ở trên lưu trữ toàn bộ nội dung của tệp1 trong hai mảng, một mảng được lập chỉ mục theo số dòng ll1[]
, một được lập chỉ mục theo nội dung dòng ss1[]
. Sau đó, khi file2 được đọc, mỗi dòng khớp sẽ bị xóa khỏi ll1[]
và ss1[]
. Cuối cùng, các dòng còn lại từ file1 là đầu ra, giữ nguyên thứ tự ban đầu.
Trong trường hợp này, với sự cố như đã nêu, bạn cũng có thể phân chia và chinh phục bằng GNU split
(lọc là phần mở rộng GNU), chạy lặp lại với các đoạn của tệp1 và đọc tệp 2 hoàn toàn mỗi lần:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
Lưu ý việc sử dụng và vị trí của -
ý nghĩa stdin
trên gawk
dòng lệnh. Điều này được cung cấp bởi split
từ file1 trong khối 20000 dòng trên mỗi lần gọi.
Đối với người dùng trên các hệ thống phi GNU, có gần như chắc chắn một coreutils GNU gói bạn có thể có được, kể cả trên OSX như một phần của của Apple Xcode công cụ cung cấp GNU diff
, awk
mặc dù chỉ là một POSIX / BSD split
chứ không phải là một phiên bản GNU.
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt