grep để tìm các phiên bản của Foo, nơi mà Bar Bar không xuất hiện trong vòng 10 dòng


10

Giả sử tôi muốn tìm kiếm toàn bộ cây cho tất cả các tệp CPP nơi xảy ra "Foo". Tôi có thể làm:

find . -name "*.cpp" | xargs grep "Foo"

Bây giờ giả sử tôi muốn vào danh sách chỉ những trường hợp mà một số chuỗi khác, nói "Bar" không không xảy ra trong vòng 3 dòng của kết quả trước đó.

Vì vậy, đưa ra hai tập tin:

a.cpp

1 Foo
2 qwerty
3 qwerty

b.cpp

1 Foo
2 Bar
3 qwerty

Tôi muốn xây dựng một tìm kiếm đơn giản trong đó tìm thấy "Foo" từ a.cpp, nhưng "Foo" từ b.cpp thì không.

Có cách nào để thực hiện điều này một cách khá đơn giản?


Có lẽ giải pháp có thể nằm trong tùy chọn grep -A và / hoặc grep -B và / hoặc grep -C. Tôi đang cố gắng nhưng không thành công ....
maurelio79

@ maurelio79: Lý thuyết hiện tại của tôi là thế này. Grep cho "Foo" bằng cách sử dụng -A 10 cho ngữ cảnh. Ống mà đến grep -v Bar. Ống mà để sed để có được tên tập tin và số dòng. Ống đó đến (một cái gì đó?) Để in dòng đó.
John Dibling

Câu trả lời:


17

Với pcregrep:

pcregrep --include='\.cpp$' -rnM 'Foo(?!(?:.*\n){0,2}.*Bar)' .

Khóa nằm trong -Mtùy chọn duy nhất pcregrepvà được sử dụng để khớp với nhiều dòng ( pcregreplấy thêm dữ liệu từ tệp đầu vào khi cần thiết khi đi bộ RE yêu cầu nó).

(?!...)là toán tử RE nhìn về phía trước perl / PCRE. Foo(?!...)phù hợp Foomiễn là ...không phù hợp với những gì sau đây.

...được (?:.*\n){0,2}.*Bar( .không khớp với một ký tự dòng mới), đó là từ 0 đến 2 dòng theo sau là một dòng chứa Bar.


+1: Tuyệt vời. Cám ơn rất nhiều; Tôi chắc chắn không dễ để tìm ra regex chính xác. Tôi rất đánh giá cao những nỗ lực của bạn. Điều này dường như được làm việc chính xác như tôi muốn.
John Dibling

2
Câu hỏi phụ nếu bạn quan tâm để trả lời. Làm thế nào bạn đến để biết về pcregrep? Tôi chưa bao giờ nghe nói về nó trước đây.
John Dibling

@JohnDibling, cá nhân tôi phát hiện ra thời gian gần đây trên unix.SE . RE đó không đặc biệt phức tạp, đặc biệt là khi bạn quen thuộc với toán tử RE (?!...)nhìn phía trước tiêu cực perl.
Stéphane Chazelas

9

Đừng bận tâm, chỉ cần sử dụng pcregreptheo đề xuất của @StephaneChazelas.


Điều này sẽ làm việc:

$ find . -name "*.cpp" | 
    while IFS= read -r file; do 
      grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
    done 

Ý tưởng là sử dụng -Acông tắc của grep để xuất các dòng khớp và N dòng sau. Sau đó, bạn chuyển kết quả qua a grep Barvà nếu điều đó không khớp (exit> 0), thì bạn lặp lại tên của tệp.

Nếu bạn biết bạn có tên tệp lành mạnh (không có dấu cách, dòng mới hoặc các ký tự lạ khác), bạn có thể đơn giản hóa thành:

$ for file in $(find . -name "*.cpp"); do 
   grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; 
  done 

Ví dụ:

terdon@oregano foo $ cat a.cpp 
1 Foo
2 qwerty
3 qwerty
terdon@oregano foo $ cat b.cpp 
1 Foo
2 Bar
3 qwerty
terdon@oregano foo $ cat c.cpp 
1 Foo
2 qwerty
3 qwerty
4 qwerty
5. Bar
terdon@oregano foo $ for file in $(find . -name "*.cpp"); do grep -A 3 Foo "$file" | grep -q Bar || echo "$file"; done 
./c.cpp
./a.cpp

Lưu ý rằng c.cppđược trả về mặc dù có chứa Barbởi vì dòng với Barhơn 3 dòng sau Foo. Bạn cna kiểm soát số lượng dòng bạn muốn tìm kiếm bằng cách thay đổi giá trị được chuyển sang -A:

$ for file in $(find . -name "*.cpp"); do 
   grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done 
./a.cpp

Đây là một cái ngắn hơn (giả sử bạn sử dụng bash):

$ shopt -s globstar 
$ for file in **/*cpp; do 
    grep -A 10 Foo "$file" | grep -q Bar || echo "$file"; 
  done

QUAN TRỌNG

Như Stephane Chazelas đã chỉ ra trong các bình luận, các giải pháp trên cũng sẽ in các tệp không chứa Foogì cả. Điều này tránh rằng:

for file in **/*cpp; do 
  grep -qm 1 Foo "$file" && 
  (grep -A 3 Foo "$file" | grep -q Bar || echo "$file"); 
done

+1 gọn gàng. Ít phức tạp hơn tôi mong đợi, nhưng không tệ chút nào.
John Dibling

Điều đó giả sử "Foo" chỉ xảy ra một lần. Điều đó cũng sẽ báo cáo các tập tin không chứa Foo. Bạn đã thiếu dấu ngoặc kép.
Stéphane Chazelas

@StephaneChazelas cảm ơn, báo giá cố định. Bạn hoàn toàn đúng về việc báo cáo các tệp không có Foovà tôi đã sửa nó nhưng tôi không thấy quan điểm của bạn về nhiều trường hợp Foo. Nó nên đối phó với họ một cách chính xác.
terdon

@JohnDibling xem cập nhật.
terdon

1
Nó sẽ không báo cáo một tệp chứa 100 dòng "Foo" theo sau là "Bar".
Stéphane Chazelas

0

Chưa được kiểm tra, tôi đang nghe điện thoại của mình:

find . -name "*.cpp" | xargs awk '/foo/{t=$0;c=10}/bar/{c=0;t=""}c{c--}t&&!c{print t;t=""}END&&t{print t}' 

một cái gì đó như thế

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.