Làm cách nào để grep cho văn bản trong một tệp và hiển thị đoạn có văn bản?


24

Dưới đây là văn bản trong tập tin:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Tôi cần grep cho "42B" và nhận đầu ra từ văn bản trên như:

Pseudo name=Apple
Code=42B
state=fault

Có ai có ý tưởng làm thế nào để đạt được điều này bằng cách sử dụng grep/ awk/ sed?


Bạn đã gắn thẻ câu hỏi này chỉ với "grep". Bạn chỉ đang tìm kiếm giải pháp "grep"? Trong câu hỏi bạn chỉ định awk & sed quá. Chúng ta có thể thêm các thẻ đó không? Tôi không chắc ý định của bạn khi tôi chỉnh sửa câu hỏi tối qua.
slm

Câu trả lời:


38

Với awk

awk -v RS='' '/42B/' file

RS=thay đổi dấu tách bản ghi đầu vào từ dòng mới thành dòng trống. Nếu bất kỳ trường nào trong một bản ghi chứa /42B/in bản ghi.

''(chuỗi null) là một giá trị ma thuật được sử dụng để thể hiện các dòng trống theo POSIX :

Nếu RS là null, thì các bản ghi được phân tách bằng các chuỗi bao gồm <newline>một hoặc nhiều dòng trống, các dòng trống dẫn hoặc theo dõi sẽ không dẫn đến các bản ghi trống ở đầu hoặc cuối của đầu vào và <newline>luôn luôn là một dấu tách trường, bất kể giá trị của FS là gì.

Các đoạn đầu ra sẽ không được tách riêng vì dấu tách đầu ra vẫn là một dòng mới. Để đảm bảo rằng có một dòng trống giữa các đoạn đầu ra, hãy đặt dấu tách bản ghi đầu ra thành hai dòng mới:

awk -v RS='' -v ORS='\n\n' '/42B/' file

1
+1 cho một giải pháp thanh lịch. Bạn không cần phải chuyển hướng tập tin mặc dù ...
jasonwryan

ngón tay đã ở chế độ lái tự động.
llua

2
@jasonwryan, trừ khi bạn cần quyền truy cập vào tên tệp trong awk ( FILENAME), không nên sử dụng chuyển hướng vì điều đó tránh được các vấn đề cho tên tệp có chứa =hoặc bắt đầu bằng -(hoặc -), tạo ra các thông báo lỗi nhất quán và tránh chạy awkhoặc thực hiện các chuyển hướng khác nếu không thể mở tệp đầu vào.
Stéphane Chazelas

14

Giả sử dữ liệu được cấu trúc sao cho luôn luôn là dòng trước và sau mà bạn muốn, bạn có thể sử dụng các công tắc -A(sau) và -B(trước) của grep để bảo nó bao gồm 1 dòng trước khớp và 1 dòng sau nó:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Nếu bạn muốn các dòng số giống nhau trước và sau cụm từ tìm kiếm, bạn có thể sử dụng công tắc -C(ngữ cảnh):

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Nếu bạn muốn nghiêm ngặt hơn khi khớp nhiều dòng bạn có thể sử dụng công cụ pcregrep, để khớp với một mẫu trên nhiều dòng:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

Các mẫu trên phù hợp như sau:

  • -M - nhiều dòng
  • 'Pseudo.*\n.*42B.*\nstate.*'- khớp với một nhóm các chuỗi trong đó chuỗi đầu tiên bắt đầu bằng từ được "Pseudo"theo sau bởi bất kỳ ký tự nào cho đến hết dòng \n, theo sau là bất kỳ ký tự nào cho đến khi chuỗi "42B"theo sau bởi bất kỳ ký tự nào cho đến cuối dòng khác ( \n), theo sau là chuỗi "state"tiếp theo là bất kỳ nhân vật nào.

5
-C(bối cảnh) có thể được sử dụng như một phím tắt, nếu -A-Bgiống nhau.
David Baggerman

@DavidBaggerman - cảm ơn. Đã thêm nó vào câu trả lời.
slm

Tại sao một người bỏ phiếu? Điều này trả lời câu hỏi.
slm

4

Có lẽ có một cách dễ dàng tương tự để làm điều đó với awk, nhưng trong perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

Điều đó về cơ bản có nghĩa là chia tệp thành các đoạn được phân tách bằng các dòng trống, sau đó chỉ in các đoạn đó khớp với biểu thức thông thường của bạn.


10
Điều này có thể được đơn giản hóa bằng cách sử dụng các tùy chọn và tốc ký, và mất việc sử dụng vô dụngcat ; perl -00 -ne 'print if /42B/' file
tripleee

4

Một grepsố hương vị của Unix có -pcờ cho "đoạn". Tôi biết AIX nào .

grep -p 42B <myfile>

sẽ làm chính xác những gì bạn yêu cầu ở đó. YMMV và GNU grep không có cờ này.


Có cờ -p sẽ là tuyệt vời. Đặc biệt nếu được sử dụng cùng với -v để bạn có thể loại trừ toàn bộ đoạn văn khỏi đầu ra.
IllvilJa

2

Một giải pháp perl khác, không có dòng trống:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Thí dụ

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

1
Hoặc ngắn hơn (và do đó dễ đọc hơn), như triplee đã viết trong một bình luận : perl -00 -ne 'print if /42B/' file.
mivk
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.