Regex lookahead cho 'không được theo sau bởi' trong grep


103

Tôi đang cố gắng gửi email cho tất cả các trường hợp Ui\.không theo sau Linehoặc thậm chí chỉ là chữ cáiL

Cách thích hợp để viết một regex để tìm tất cả các trường hợp của một chuỗi cụ thể KHÔNG được theo sau bởi một chuỗi khác là gì?

Sử dụng trang đầu

grep "Ui\.(?!L)" *
bash: !L: event not found


grep "Ui\.(?!(Line))" *
nothing

5
Những loài phụ nào của regex - PCRE, ERE, BRE, grep, ed, sed, perl, python, Java, C, ...?
Jonathan Leffler

4
Ngoài ra, "sự kiện không tìm thấy" đến từ việc sử dụng mở rộng lịch sử. Bạn có thể muốn tắt mở rộng lịch sử nếu bạn không bao giờ sử dụng nó và đôi khi muốn có thể sử dụng dấu chấm than trong các lệnh tương tác của mình. set +o histexpandtrong Bash hoặc set +H, YMMV.
tripleee

12
Tôi cũng có vấn đề mở rộng lịch sử. Tôi nghĩ rằng tôi đã giải quyết nó đơn giản bằng cách chuyển sang các dấu ngoặc kép đơn lẻ, vì vậy shell sẽ không cố gắng làm xáo trộn đối số.
Người lập mã

@Coderer cũng đã giải quyết được vấn đề của tôi. Cảm ơn.
NHDaly

Câu trả lời:


151

Cái nhìn tiêu cực, cái bạn đang theo đuổi, yêu cầu một công cụ mạnh hơn tiêu chuẩn grep. Bạn cần một grep hỗ trợ PCRE.

Nếu bạn có GNU grep, phiên bản hiện tại hỗ trợ các tùy chọn -Phoặc --perl-regexpsau đó bạn có thể sử dụng regex mà bạn muốn.

Nếu bạn không có GNU (phiên bản đủ gần đây) grep, thì hãy cân nhắc việc tải xuống ack.


37
Tôi khá chắc chắn rằng vấn đề trong trường hợp này chỉ là trong bash, bạn nên sử dụng dấu nháy đơn chứ không phải dấu nháy kép để nó không được coi !là một ký tự đặc biệt.
NHDaly

(xem bên dưới để biết câu trả lời của tôi mô tả chính xác điều đó.)
NHDaly

4
Câu trả lời đúng, đã được xác minh nên kết hợp câu trả lời này và nhận xét của @ NHDaly. Ví dụ: lệnh này phù hợp với tôi: grep -P '^. * Chứa ((?! But_not_this).) * $' * .Log. *> "D: \ temp \ result.out"
wangf

3
Đối với những nơi -Pkhông được hỗ trợ kết quả ống thử một lần nữa để grep --invert-match, ví dụ: git log --diff-filter=D --summary | grep -E 'delete.*? src' | grep -E --invert-match 'xml'. Đảm bảo ủng hộ câu trả lời của @Vinicius Ottoni.
Daniel Sokolowski

@wangf Tôi đang sử dụng Bash trong Cygwin và khi tôi thay đổi thành dấu ngoặc kép, tôi vẫn gặp lỗi "không tìm thấy sự kiện".
SSilk

39

Câu trả lời cho một phần vấn đề của bạn là ở đây và ack sẽ hành xử theo cách tương tự: Ack & negative lookahead đưa ra lỗi

Bạn đang sử dụng dấu ngoặc kép cho grep, cho phép bash "diễn giải !như lệnh mở rộng lịch sử".

Bạn cần bao gồm mẫu của mình trong SINGLE-QUOTES: grep 'Ui\.(?!L)' *

Tuy nhiên, hãy xem câu trả lời của @ JonathanLeffler để giải quyết các vấn đề với cái nhìn tiêu cực theo tiêu chuẩn grep!


Bạn đang nhầm lẫn chức năng mở rộng của GNU grepvới chức năng của tiêu chuẩn grep, trong đó tiêu chuẩn greplà POSIX. Những gì bạn nói cũng đúng - Tôi chạy Bash với tính năng man rợ của C-shell bị vô hiệu hóa (vì nếu tôi muốn có C shell, tôi sẽ sử dụng một cái, nhưng tôi không muốn một cái), vì vậy những !thứ không ảnh hưởng đến tôi - nhưng để có được những cái nhìn tiêu cực, bạn cần không chuẩn grep.
Jonathan Leffler

1
@JonathanLeffler, cảm ơn bạn đã làm rõ; Tôi nghĩ bạn đúng rằng nó đòi hỏi cả hai câu trả lời của chúng tôi để giải quyết tất cả các triệu chứng của OP. Cảm ơn.
NHDaly

10

Có thể bạn không thể thực hiện tiêu chuẩn tìm kiếm phủ định bằng grep, nhưng thông thường bạn sẽ có thể có được hành vi tương đương bằng cách sử dụng công tắc "nghịch đảo" '-v'. Sử dụng nó, bạn có thể xây dựng một regex cho phần bổ sung của những gì bạn muốn khớp và sau đó chuyển nó qua 2 greps.

Đối với regex được đề cập, bạn có thể làm điều gì đó như

grep 'Ui\.' * | grep -v 'Ui\.L'

Điều đó sẽ loại trừ những thứ hơn, ví dụ nếu dòng chứa Ui.Line và Ui mà không .Line
nafg

1
(Vâng, đó là lý do tại sao tôi không xây dựng nó đúng này chỉ đơn giản là giải quyết phần lớn các kịch bản mà người điều hướng cho vấn đề này, không có gì nhiều hơn nữa..)
Karel Tucek

4

Nếu bạn cần sử dụng triển khai regex không hỗ trợ các tiêu đề phủ định và bạn không ngại đối sánh (các) ký tự phụ *, thì bạn có thể sử dụng các lớp ký tự phủ định[^L] , thay thế|ký tự cuối chuỗi$ .

Trong trường hợp của bạn grep 'Ui\.\([^L]\|$\)' *thực hiện công việc.

  • Ui\. khớp với chuỗi mà bạn quan tâm

  • \([^L]\|$\)khớp với bất kỳ ký tự đơn nào khác với Lhoặc nó khớp với cuối dòng: [^L]hoặc $.

Nếu bạn muốn loại trừ nhiều hơn chỉ một ký tự, thì bạn chỉ cần ném thêm xen kẽ và phủ định vào nó. Để tìm akhông theo sau bc:

grep 'a\(\([^b]\|$\)\|\(b\([^c]\|$\)\)\)' *

Đó là hoặc ( atheo sau bởi không bhoặc theo sau bởi cuối dòng: asau đó [^b]hoặc $) hoặc ( atheo sau blà hoặc được theo sau bởi không choặc được theo sau bởi cuối dòng: asau đó b, sau đó [^c]hoặc $.

Loại biểu thức này khá khó sử dụng và dễ xảy ra lỗi với ngay cả một chuỗi ngắn. Bạn có thể viết một cái gì đó để tạo các biểu thức cho mình, nhưng có lẽ sẽ dễ dàng hơn nếu chỉ sử dụng triển khai regex hỗ trợ các trang đầu phủ định.

* Nếu việc triển khai của bạn hỗ trợ các nhóm không chụp thì bạn có thể tránh chụp thêm các ký tự.


1

Nếu grep của bạn không hỗ trợ -P hoặc --perl-regexp và bạn có thể cài đặt grep hỗ trợ PCRE, ví dụ: "pcregrep", thì nó sẽ không cần bất kỳ tùy chọn dòng lệnh nào như GNU grep để chấp nhận Perl tương thích thông thường biểu thức, bạn chỉ cần chạy

pcregrep "Ui\.(?!Line)"

Bạn không cần một nhóm lồng nhau khác cho "Dòng" như trong ví dụ "Ui. (?! (Dòng))" - nhóm bên ngoài là đủ, như tôi đã trình bày ở trên.

Hãy để tôi cung cấp cho bạn một ví dụ khác về xác nhận phủ định: khi bạn có danh sách các dòng, được trả về bởi "ipset", mỗi dòng hiển thị số lượng gói ở giữa dòng và bạn không cần các dòng có 0 gói, bạn chỉ cần chạy:

ipset list | pcregrep "packets(?! 0 )"

Nếu bạn thích các biểu thức chính quy tương thích với perl và có perl nhưng không có pcregrep hoặc grep của bạn không hỗ trợ --perl-regexp, bạn có thể có các tập lệnh perl một dòng hoạt động giống như grep:

perl -e "while (<>) {if (/Ui\.(?!Lines)/){print;};}"

Perl chấp nhận stdin theo cách tương tự như grep, ví dụ:

ipset list | perl -e "while (<>) {if (/packets(?! 0 )/){print;};}"
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.