2012-02-08 20 views
73

Tôi đang cố gắng để grep cho tất cả các trường hợp của Ui\. không tiếp theo Line hoặc thậm chí chỉ thư LRegex lookahead cho 'không tiếp theo' trong grep

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

Sử dụng lookaheads

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


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

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

+0

Sử dụng nó với grep. –

+4

Như một sang một bên, "sự kiện không tìm thấy" xuất phát 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 histexpand' trong Bash hoặc' set + H', YMMV. – tripleee

Trả lời

104

Negative lookahead, đó là những gì bạn sau đó, đòi hỏi một công cụ mạnh mẽ hơn so với tiêu chuẩn grep. Bạn cần một grep PCRE kích hoạt.

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

Nếu bạn không có (phiên bản đầy đủ gần đây) GNU grep, sau đó cân nhắc việc nhận ack.

+29

Tôi khá chắc chắn vấn đề trong trường hợp này chỉ là trong bash bạn nên sử dụng dấu ngoặc đơn không phải dấu ngoặc kép để nó sẽ không đối xử với '!' Như một nhân vật đặc biệt. – NHDaly

+0

(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

+1

Được xác minh, câu trả lời đúng 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 làm việc cho tôi: ** grep -P '^. * Chứa ((?! But_not_this).) * $' \ *. Log. *> "D: \ temp \ result.ra "** – wangf

30

Câu trả lời cho một phần của vấn đề của bạn là ở đây, và ack sẽ hành xử theo cùng một cách: "giải thích ! như lịch sử mở rộng lệnh" Ack & negative lookahead giving errors

Bạn đang sử dụng hai dấu ngoặc kép cho grep, cho phép bash để

Bạn cần phải quấn mô hình của bạn trong dấu chú giải đơn: grep 'Ui\.(?!L)' *

Tuy nhiên, xem @JonathanLeffler's answer để giải quyết các vấn đề với lookaheads tiêu cực trong tiêu chuẩn grep!

+0

Bạn đang bối rối về chức năng mở rộng của GNU' grep' với chức năng của 'grep' chuẩn, trong đó tiêu chuẩn cho [' grep'] (http://pubs.opengroup.org)/onlinepubs/9699919799/utilit ies/grep.html) là POSIX. Những gì bạn nói cũng đúng - tôi chạy Bash với các rào cản C-shell bị vô hiệu hóa (bởi vì nếu tôi muốn một trình bao C, tôi sẽ sử dụng một trình bao, nhưng tôi không muốn một), vì vậy, công cụ '!' Không ảnh hưởng đến tôi - nhưng để có được cái nhìn tiêu cực, bạn không cần chuẩn 'grep'. –

+0

@JonathanLeffler, cảm ơn vì đã làm rõ; Tôi nghĩ rằng 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

4

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

Đối với regex trong câu hỏi bạn có thể làm điều gì đó như

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

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

+0

(Vâng, đó là lý do tại sao tôi không xây dựng nó một cách nghiêm túc. hơn.) –

3

Nếu bạn cần phải sử dụng một thực hiện regex mà không hỗ trợ lookaheads tiêu cực và bạn không nhớ phù hợp với nhân vật phụ (s) *, sau đó bạn có thể sử dụng negated character classes [^L], alternation |end of string anchor $.

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

  • Ui\. phù hợp với chuỗi bạn quan tâm đến

  • \([^L]\|$\) khớp với bất kỳ ký tự đơn khác hơn L hoặc nó phù hợ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ự, bạn chỉ cần ném nhiều thay đổi và phủ định ở đó. Để tìm a không tiếp theo bc:

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

Đó là một trong hai (a tiếp theo không b hoặc tiếp theo là cuối dòng: a sau đó [^b] hoặc $) hoặc (a Tiếp theo b đó là một trong hai theo sau là không c hoặc được theo sau bởi cuối dòng: a rồi b, sau đó [^c] hoặc $.

Loại biểu thức này trở nên khá khó sử dụng và dễ bị lỗi thậm chí là một chuỗi ngắn. Bạn có thể viết một cái gì đó để tạo ra các biểu thức cho bạn, nhưng nó có thể được dễ dàng hơn để chỉ cần sử dụng một thực hiện regex hỗ trợ lookaheads tiêu cực.

* Nếu triển khai của bạn hỗ trợ non-capturing groups thì bạn có thể tránh chụp các ký tự thừa.

0

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 PCRE, ví dụ: "Pcregrep", hơn nó sẽ không cần bất kỳ tùy chọn dòng lệnh như GNU grep để chấp nhận biểu thức thông thường Perl-tương thích, bạn chỉ cần chạy

pcregrep "Ui\.(?!Line)" 

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

Để tôi cung cấp cho bạn một ví dụ khác về xác nhận tiêu cực: 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 đường và bạn không cần dòng bằng 0 các gói dữ liệu, bạn chỉ cần chạy:

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

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

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

Perl chấp nhận stdi n giống như grep, ví dụ:

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