Với grep, làm thế nào tôi có thể khớp một mẫu và đảo ngược với mẫu khác?


11

Với grep, tôi muốn chọn tất cả các dòng khớp với một mẫu và không khớp với mẫu khác. Tôi muốn có thể sử dụng một lệnh gọi duy nhất grepđể tôi có thể sử dụng --after-contexttùy chọn (hoặc --before-context, hoặc --context).

-vkhông khả thi ở đây, vì nó phủ nhận tất cả các mẫu tôi chuyển sang grepsử dụng -etùy chọn.

Thí dụ

Tôi muốn tìm kiếm các dòng khớp needle, bỏ qua các dòng khớp ignore me, với một dòng của bối cảnh sau.

Đây là tập tin đầu vào của tôi:

one needle ignore me
two
three
four needle
five

Đầu ra tôi muốn là:

four needle
five

Như bạn có thể thấy, giải pháp ngây thơ này không hoạt động:

$ cat file | grep --after-context=1 needle | grep -v 'ignore me'
two
---
four needle
five

Câu trả lời:


10

Nếu bạn có GNU grep, bạn có thể sử dụng các biểu thức chính quy Perl , có cấu trúc phủ định .

grep -A1 -P '^(?!.*ignore me).*needle'

Nếu bạn không có GNU grep, bạn có thể mô phỏng các tùy chọn ngữ cảnh trước / sau trong awk .

awk -v after=3 -v before=2 '
/needle/ && !/ignore me/ {
    for (i in h) {
        print h[i];
        delete h[i];
    }
    until = NR + after;
}
{
    if (NR <= until) print $0; else h[NR] = $0;
    delete h[NR-before];
}
END {exit !until}
'

8

Bạn dường như đang sử dụng GNU . Với GNU grep, bạn có thể chuyển --perl-regexcờ để kích hoạt PCRE và sau đó cung cấp xác nhận nhìn tiêu cực, ví dụ bên dưới

grep --after-context=1 \
--perl-regex '^(?:(?!ignore me).)*needle(?:(?!ignore me).)*$' file.txt
four needle
five

Điều quan trọng cần lưu ý ở đây là (?:(?!STRING).)*STRINGnhư [^CHAR]*CHAR


@ 1_CR ... Thưa ngài .. thật tuyệt vời ..: P một cái gì đó smiler toack
Rahul Patil

@RahulPatil. :-), vâng GNU grep là tốt.
iruvar

Đó không phải là điều tôi muốn. Tôi muốn nó hoạt động cho dù "bỏ qua tôi" là trước hay sau "kim".
Flimm

@RahulPatil, cảm ơn, tôi đã sửa nó trong phiên bản mới nhất
iruvar

Rất hữu dụng. Đặc biệt là trong trường hợp grep với bối cảnh mà bạn muốn loại trừ các dòng khớp chặt chẽ nhưng không có một phần nhất định của mẫu. Gần với câu hỏi ban đầu nhưng không hoàn toàn giống nhau.
gaoithe

2

Tôi sẽ đề nghị sử dụng awk thay vì nó xử lý IO nhiều dòng tốt hơn. Hoặc 1) Ống kết quả cho GNU awk với --\nnhư tách hồ sơ, hoặc 2) Làm tất cả các kết hợp trong awk.

lựa chọn 1

<file grep -A1 needle | awk '!/ignore me/' RS='--\n' ORS='--\n'

Đầu ra:

four needle                                                                                  
five
--

Lưu ý, tùy chọn này tìm kiếm toàn bộ bản ghi ignore me, đặt FS=1và khớp với $1chỉ để so sánh với dòng đầu tiên.

Lựa chọn 2

<file awk 'a-- > 0; $0 ~ re1 && $0 !~ re2 { print $0; a=after }' re1=needle re2='ignore me' after=1

Có nhiều ignore metrong tập tin không, awk không hoạt động
Rahul Patil

@RahulPatil: bạn có thể viết lại hoặc thêm chi tiết cho câu hỏi của bạn không? Tôi không hiểu những gì bạn đang hỏi.
Thor

@Thos kiểm tra ví dụ của bạn với tệp đầu vào này dán.ubfox.com/6252860
Rahul Patil

@RahulPatil: Tôi hiểu ý của bạn bây giờ, Tùy chọn 1 giả định rằng một --\ndấu phân cách nằm giữa mỗi nhóm khớp, không có nếu các nhóm liền kề nhau. Làm thế nào các nhóm liền kề nên được xử lý là cụ thể nhiệm vụ, vì vậy điều này không nhất thiết là sai. Tùy chọn 2 không phụ thuộc vào dải phân cách và không bị ảnh hưởng.
Thor
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.