grep bỏ qua n dòng tệp và chỉ tìm kiếm sau


9

Tôi có một tệp nhật ký rất lớn và muốn grep sự xuất hiện đầu tiên của một mẫu, và sau đó tìm một mẫu khác ngay sau lần xuất hiện này.

Ví dụ:

123
XXY
214
ABC
182
558
ABC
856
ABC

Trong ví dụ của tôi, tôi muốn tìm 182và sau đó tìm sự xuất hiện tiếp theo củaABC

Sự xuất hiện đầu tiên rất đơn giản:

grep -n -m1 "182" /var/log/file

Kết quả này:

5:182

Làm thế nào để tôi tìm thấy sự xuất hiện tiếp theo của ABC?

Ý tưởng của tôi là bảo grepbỏ qua các ndòng đầu tiên (trong ví dụ trên n=5), dựa trên số dòng của 182. Nhưng làm thế nào để tôi làm điều đó?


1
Nó có phải là một yêu cầu grepđược sử dụng? Tôi không nghĩ rằng điều này có thể được thực hiện với grepnhưng nó sẽ dễ dàng với awkhoặc sed(một mình hoặc kết hợp với grep).
Hauke ​​Laging

@HaukeLaging grepkhông bắt buộc. Tôi chưa quá quen thuộc với sedhoặc awk. Nếu bạn có một giải pháp tốt, hãy để tôi nghe nó! :) @don_crissti chỉ nên in dòng đầu tiên. Tôi không quan tâm đến những sự kiện khác.
koljanep

Câu trả lời:


10

Với sedbạn có thể sử dụng một phạm vi và qđầu vào uit tại một lần hoàn thành:

sed '/^182$/p;//,/^ABC$/!d;/^ABC$/!d;q'

Tương tự w / GNU, grepbạn có thể phân chia đầu vào giữa hai greps:

{ grep -nxF -m1 182; grep -nxF -m1 ABC; } <<\IN
123
XXY
214
ABC
182
558
ABC
856
ABC
IN

... mà in ...

5:182
2:ABC

... để biểu thị rằng dòng đầu tiên greptìm thấy một -Fchuỗi ký tự ixed, -xtoàn bộ 182 dòng khớp với 5 dòng từ khi bắt đầu đọc và dòng thứ hai tìm thấy một dòng ABC được gõ tương tự 2 dòng từ đầu đọc - hoặc 2 dòng sau lần grep bỏ đọc đầu tiên ở dòng 5.

Từ man grep:

-m NUM, --max-count=NUM
          Stop  reading  a  file  after  NUM  matching
          lines.   If the input is standard input from
          a regular file, and NUM matching  lines  are
          output, grep ensures that the standard input
          is  positioned  to  just  after   the   last
          matching  line before exiting, regardless of
          the  presence  of  trailing  context  lines.
          This  enables  a calling process to resume a
          search. 

Tôi đã sử dụng một tài liệu ở đây để chứng minh khả năng tái tạo, nhưng có lẽ bạn nên làm:

{ grep ...; grep ...; } </path/to/log.file

Nó cũng sẽ làm việc với các cấu trúc lệnh hỗn hợp shell khác như:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file

+1 Thấy rằng trong trang người đàn ông. Đó là những gì tôi đã cố gắng, chỉ với một đường ống giữa grepthay vì ;... không đi
Xen2050

@ Xen2050 - đường ống sẽ không hoạt động, thông thường - một tệp có thể lseek thường là những gì bạn muốn khi chia sẻ đầu vào.
mikeerv

Câu trả lời ấn tượng nhưng tôi không ủng hộ tuyên bố của bạn về đường ống dẫn. Tài liệu ở đây mà hai người grepchia sẻ thực sự là một đường ống dẫn cho họ. Một cái gì đó khác: Tôi đã thử mà không in dòng đánh dấu nhưng sed '//,/^ABC$/!d;/^ABC$/!d;q'ném một lỗi lạ. Không gì //làm gì?
Hauke ​​Laging

1
@HaukeLaging - tài liệu ở đây (trong hầu hết các shell) không phải là một đường ống - đó là một tệp tmp thực được tạo bởi trình bao mà trình bao xóa trước khi thực hiện bất kỳ ghi nào - trong khi duy trì bộ mô tả. Nó vẫn còn lseekable. Ống, nói chung, không phải là lseekable. Tôi sẽ xem xét sedđiều này - chỉ cần viết nó ra thật nhanh.
mikeerv

1
@HaukeLaging - Ồ, vậy là sedcái này hoạt động - bạn chỉ cần bỏ qua phần tham khảo. Trong sedbạn có thể tham khảo lần cuối cùng /address/với một //địa chỉ trống . Vì vậy, /^182$/command;//,/next_address/chỉ cần làm /^182$/command;/^182$/,/next_address/. Lỗi của bạn có thể không có biểu thức chính quy trước đó nếu bạn đang sử dụng GNU sed. Nhân tiện, điều này có thể được điều khiển bằng cách sử dụng thông qua các /dev/fd/[num]liên kết trên các hệ thống linux - nhưng nếu bạn không cẩn thận xử lý tốt bộ đệm (như với dd) thì đó thường là một trận thua.
mikeerv

2

Sử dụng grepvới biểu thức chính quy tương thích Perl ( pcregrep):

pcregrep -Mo '182(.|\n)*?\KABC'

Tùy chọn -Mcho phép mẫu khớp với nhiều hơn một dòng và \Kkhông bao gồm mẫu phù hợp (tính đến thời điểm này) vào đầu ra. Bạn có thể xóa \Knếu bạn muốn có toàn bộ khu vực.


2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC

1
Điều đó mang lại cho ABC đầu tiên ở bất cứ đâu ; câu hỏi này muốn ABC đầu tiên sau 182 đầu tiên. Hầu hết trực tiếp là một cờ giống như awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file- hoặc bạn có thể viết ít nhất một getline()vòng lặp rõ ràng thường vụng về hoặc thông minh (?) bằng cách sử dụng một phạm vi gần giống như perl của @ JRFerguson:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thedom_085

@ dave_thndry_085 Thật vậy. Đúng ý tưởng nhưng được mã hóa khủng khiếp (trộn lẫn hai ý tưởng trong khi viết). Mặc dù tôi đã cố gắng nhưng không tự hỏi ở đầu ra.
Hauke ​​Laging

1

Một biến thể Perl bạn có thể sử dụng là:

perl -nle 'm/182/../ABC/ and print' file

... In các dòng trong phạm vi phù hợp.

Nếu tệp của bạn chứa nhiều phạm vi khớp, bạn có thể giới hạn đầu ra chỉ ở phạm vi đầu tiên bằng cách thay đổi /dấu phân cách thành?

perl -nle 'm?182?..?ABC? and print'

1

Chỉ với grepvà thêm tail& cut, bạn có thể ...

grep cho số dòng của trận đấu đầu tiên của 182:

grep -m 1 -n 182 /var/log/file |cut -f1 -d:

Sử dụng điều đó để grep cho tất cả các ABCchỉ sau dòng khớp đầu tiên ở trên, sử dụng tail' -n +Kđể xuất ra sau dòng thứ K'th. Tất cả cùng nhau:

tail -n +$(grep -m 1 -n 182 /var/log/file |cut -f1 -d:) /var/log/file | grep ABC

Hoặc thêm -m 1một lần nữa để chỉ tìm kết quả khớp đầu tiênABC

tail -n +$(grep -m 1 -n 182 /var/log/file|cut -f1 -d:) /var/log/file|grep -m 1 ABC

Tài liệu tham khảo:
mantrang
/programming/6958841/use-grep-to-report-back-only-line-numbers


1

Một biến thể khác là:

grep -n -A99999 "182" /var/log/file|grep -n -m1 "ABC"

Cờ -An greps n dòng sau trận đấu và 99999 chỉ để đảm bảo chúng tôi không bỏ lỡ bất cứ điều gì. Các tệp lớn hơn nên có nhiều dòng hơn (kiểm tra với "wc -l").


0

Toán tử phạm vi ,có thể được đưa vào sử dụng ở đây:

< yourfile \
sed -e '
   /182/,/ABC/!d
   //!d;=;/ABC/q
' | sed -e 'N;s/\n/:/'

Toán tử phạm vi ..song song với toán tử chỉ khớp một lần m??có thể được đưa vào sử dụng ở đây trongPerl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile
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.