Làm cách nào để xóa các dòng xuất hiện trên tệp B khỏi tệp A khác?


160

Tôi có một tệp lớn A (bao gồm các email), một dòng cho mỗi thư. Tôi cũng có một tập tin B khác chứa một tập hợp các thư khác.

Lệnh nào tôi sẽ sử dụng để xóa tất cả các địa chỉ xuất hiện trong tệp B khỏi tệp A.

Vì vậy, nếu tệp A chứa:

A
B
C

và tập tin B chứa:

B    
D
E

Sau đó, tập tin A nên được để lại với:

A
C

Bây giờ tôi biết đây là một câu hỏi có thể đã được hỏi thường xuyên hơn, nhưng tôi chỉ tìm thấy một lệnh trực tuyến khiến tôi gặp lỗi với một dấu phân cách xấu.

Bất kì sự trợ giúp nào đều được đánh giá cao! Ai đó chắc chắn sẽ đưa ra một lớp lót thông minh, nhưng tôi không phải là chuyên gia về vỏ.



1
Hầu hết nếu các câu trả lời ở đây là dành cho các tệp được sắp xếp, và câu trả lời rõ ràng nhất bị thiếu, tất nhiên đó không phải là lỗi của bạn, nhưng điều đó làm cho cái khác nói chung hữu ích hơn.
tripleee

Câu trả lời:


202

Nếu các tệp được sắp xếp (chúng nằm trong ví dụ của bạn):

comm -23 file1 file2

-23chặn các dòng trong cả hai tệp hoặc chỉ trong tệp 2. Nếu các tệp không được sắp xếp, hãy chuyển chúng qua sort...

Xem trang người đàn ông ở đây


8
comm -23 file1 file2 > file3sẽ xuất nội dung trong tệp1 chứ không phải trong tệp2, sang tệp3. Và mv file3 file1cuối cùng sẽ xóa nội dung dư thừa trong tệp1.
Quang phổ

2
Cách khác, sử dụng comm -23 file1 file2 | sponge file1. Không cần dọn dẹp.
Socowi

Liên kết trang người dùng không tải cho tôi - thay thế: linux.die.net/man/1/comm
Felix Rabe

@Socowi Bọt biển là gì? Tôi không có cái đó trên hệ thống của mình. (macos 10.13)
Felix Rabe

@FelixRabe, tốt, đó là mệt mỏi. Thay thế bằng liên kết của bạn. Cảm ơn
Archetypal Paul

84

grep -Fvxf <lines-to-remove> <all-lines>

  • hoạt động trên các tệp không được sắp xếp
  • duy trì trật tự
  • là POSIX

Thí dụ:

cat <<EOF > A
b
1
a
0
01
b
1
EOF

cat <<EOF > B
0
1
EOF

grep -Fvxf B A

Đầu ra:

b
a
01
b

Giải trình:

  • -F: sử dụng chuỗi ký tự thay vì BRE mặc định
  • -x: chỉ xem xét các trận đấu khớp với toàn bộ dòng
  • -v: in không khớp
  • -f file: lấy mẫu từ tệp đã cho

Phương pháp này chậm hơn trên các tệp được sắp xếp trước so với các phương pháp khác, vì nó chung chung hơn. Nếu tốc độ cũng quan trọng, hãy xem: Cách nhanh chóng để tìm dòng trong một tệp không nằm trong tệp khác?

Đây là một tự động bash nhanh cho hoạt động nội tuyến:

remove-lines() (
  remove_lines="$1"
  all_lines="$2"
  tmp_file="$(mktemp)"
  grep -Fvxf "$remove_lines" "$all_lines" > "$tmp_file"
  mv "$tmp_file" "$all_lines"
)

GitHub ngược dòng .

sử dụng:

remove-lines lines-to-remove remove-from-this-file

Xem thêm: /unix/28158/is-there-a-tool-to-get-the-lines-in-one-file-that-are-not-in-another


55

awk để giải cứu!

Giải pháp này không yêu cầu đầu vào được sắp xếp. Bạn phải cung cấp fileB trước.

awk 'NR==FNR{a[$0];next} !($0 in a)' fileB fileA

trả lại

A
C

Làm thế nào nó hoạt động?

NR==FNR{a[$0];next} thành ngữ là để lưu trữ tệp đầu tiên trong một mảng kết hợp làm khóa cho bài kiểm tra "chứa" sau này.

NR==FNR đang kiểm tra xem chúng tôi có đang quét tệp đầu tiên hay không, trong đó bộ đếm dòng toàn cầu (NR) bằng với bộ đếm dòng tệp hiện tại (FNR).

a[$0] thêm dòng hiện tại vào mảng kết hợp làm khóa, lưu ý rằng điều này hoạt động giống như một tập hợp, trong đó sẽ không có bất kỳ giá trị trùng lặp (khóa) nào

!($0 in a)Bây giờ chúng tôi đang ở (các) tệp tiếp theo, inlà một thử nghiệm chứa, ở đây nó kiểm tra xem dòng hiện tại có nằm trong tập hợp mà chúng tôi đã điền trong bước đầu tiên từ tệp đầu tiên hay không, !phủ nhận điều kiện. Điều còn thiếu ở đây là hành động, theo mặc định là {print}và thường không được viết rõ ràng.

Lưu ý rằng điều này bây giờ có thể được sử dụng để loại bỏ các từ trong danh sách đen.

$ awk '...' badwords allwords > goodwords

với một thay đổi nhỏ, nó có thể xóa nhiều danh sách và tạo các phiên bản đã được làm sạch.

$ awk 'NR==FNR{a[$0];next} !($0 in a){print > FILENAME".clean"}' bad file1 file2 file3 ...

điểm đầy đủ về điều này. Để sử dụng điều này trên dòng lệnh trong GnuWin32 trong Windows, thay thế các ngòi đơn bằng dấu ngoặc kép. làm việc một điều trị. cảm ơn nhiều.
Twobob

Điều này hoạt động nhưng làm thế nào tôi có thể chuyển hướng đầu ra sang tệpA ở dạng A (Với một dòng mới) B
Nhà xây dựng Anand

Tôi đoán bạn có nghĩa là A\nC, viết vào một tệp tạm thời trước và ghi đè lên tệp gốc... > tmp && mv tmp fileA
karakfa

Điểm đầy đủ trong này từ tôi quá. Awk này mất tất cả 1 giây để xử lý một tệp với 104.000 mục: +1:
MitchellK

Khi sử dụng điều này trong các tập lệnh, trước tiên hãy đảm bảo kiểm tra xem nó fileBkhông trống (dài 0 byte), vì nếu có, bạn sẽ nhận được kết quả trống thay vì nội dung dự kiến fileA. (Nguyên nhân: FNR==NRsẽ áp dụng cho fileAsau đó.)
Peter Nowee

18

Một cách khác để làm điều tương tự (cũng yêu cầu đầu vào được sắp xếp):

join -v 1 fileA fileB

Trong Bash, nếu các tệp không được sắp xếp trước:

join -v 1 <(sort fileA) <(sort fileB)

7

Bạn có thể làm điều này trừ khi các tệp của bạn được sắp xếp

diff file-a file-b --new-line-format="" --old-line-format="%L" --unchanged-line-format="" > file-a

--new-line-formatdành cho các dòng trong tệp b nhưng không phải trong a --old-..là dành cho các dòng trong tệp a nhưng không phải trong b --unchanged-..là dành cho các dòng nằm trong cả hai. %Llàm cho nó để dòng được in chính xác.

man diff

để biết thêm chi tiết


1
Bạn nói điều này sẽ hoạt động trừ khi các tập tin được sắp xếp. Những vấn đề xảy ra nếu chúng được sắp xếp? Nếu chúng được sắp xếp một phần thì sao?
Carlos Macasaet

1
Đó là phản ứng với giải pháp trên mà đề xuất sử dụng commlệnh. commyêu cầu các tệp được sắp xếp, vì vậy nếu chúng được sắp xếp, bạn cũng có thể sử dụng giải pháp đó. Bạn có thể sử dụng giải pháp này bất kể tập tin có được sắp xếp hay không
vào

7

Sự sàng lọc câu trả lời hay của @ karakfa có thể nhanh hơn đáng kể đối với các tệp rất lớn. Như với câu trả lời đó, không cần phải sắp xếp tệp, nhưng tốc độ được đảm bảo nhờ các mảng kết hợp của awk. Chỉ có tệp tra cứu được giữ trong bộ nhớ.

Công thức này cũng cho phép khả năng chỉ có một trường cụ thể ($ N) trong tệp đầu vào được sử dụng trong so sánh.

# Print lines in the input unless the value in column $N
# appears in a lookup file, $LOOKUP;
# if $N is 0, then the entire line is used for comparison.

awk -v N=$N -v lookup="$LOOKUP" '
  BEGIN { while ( getline < lookup ) { dictionary[$0]=$0 } }
  !($N in dictionary) {print}'

(Một ưu điểm khác của phương pháp này là dễ dàng sửa đổi tiêu chí so sánh, ví dụ: cắt bớt khoảng trắng hàng đầu và dấu.)


Điều này khó sử dụng hơn trong kịch bản đa nền tảng góc cạnh so với kịch bản khác. Tuy nhiên,
ngả

2

Bạn có thể sử dụng Python:

python -c '
lines_to_remove = set()
with open("file B", "r") as f:
    for line in f.readlines():
        lines_to_remove.add(line.strip())

with open("file A", "r") as f:
    for line in [line.strip() for line in f.readlines()]:
        if line not in lines_to_remove:
            print(line)
'

2

Bạn có thể dùng - diff fileA fileB | grep "^>" | cut -c3- > fileA

Điều này sẽ làm việc cho các tập tin không được sắp xếp là tốt.


-1

Để xóa các dòng chung giữa hai tệp, bạn có thể sử dụng lệnh grep, comm hoặc tham gia.

grep chỉ hoạt động cho các tập tin nhỏ. Sử dụng -v cùng với -f.

grep -vf file2 file1 

Điều này sẽ hiển thị các dòng từ tệp1 không khớp với bất kỳ dòng nào trong tệp2.

comm là một lệnh tiện ích hoạt động trên các tập tin được sắp xếp theo từ vựng. Nó nhận hai tệp làm đầu vào và tạo ra ba cột văn bản làm đầu ra: các dòng chỉ trong tệp đầu tiên; dòng chỉ trong tập tin thứ hai; và dòng trong cả hai tập tin. Bạn có thể chặn in bất kỳ cột nào bằng cách sử dụng tùy chọn -1, -2 hoặc -3 tương ứng.

comm -1 -3 file2 file1

Điều này sẽ hiển thị các dòng từ tệp1 không khớp với bất kỳ dòng nào trong tệp2.

Cuối cùng, có phép nối, một lệnh tiện ích thực hiện phép nối đẳng thức trên các tệp đã chỉ định. Tùy chọn -v của nó cũng cho phép xóa các dòng chung giữa hai tệp.

join -v1 -v2 file1 file2

Tất cả những điều này đã được đưa ra trong các câu trả lời khác. Grep của bạn cần một -F, hoặc bạn sẽ nhận được kết quả kỳ lạ khi các dòng trông giống như biểu thức chính quy
Archetypal Paul
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.