grep lewatkan n baris file dan hanya mencari setelah

9

Saya memiliki file log yang sangat besar dan ingin memahami kemunculan pola yang pertama, dan kemudian menemukan pola lain tepat setelah kejadian ini.

Sebagai contoh:

123
XXY
214
ABC
182
558
ABC
856
ABC

Dalam contoh saya, saya ingin mencari 182dan kemudian menemukan kejadian berikutnyaABC

Kejadian pertama sederhana:

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

Output ini:

5:182

Bagaimana saya menemukan kemunculan ABC selanjutnya?

Ide saya adalah memberitahu grepuntuk melewati nbaris pertama (dalam contoh di atas n=5), berdasarkan nomor baris 182. Tetapi bagaimana saya melakukannya?

koljanep
sumber
1
Apakah itu persyaratan yang grepdigunakan? Saya tidak berpikir ini bisa dilakukan dengan greptetapi akan mudah dengan awkatau sed(sendiri atau dalam kombinasi dengan grep).
Hauke ​​Laging
@ HaukeLaging greptidak diperlukan. Saya belum begitu akrab dengan sedatau awk. Jika Anda memiliki solusi yang baik, biarkan saya mendengarnya! :) @don_crissti hanya baris pertama yang harus dicetak. Saya tidak peduli dengan kejadian lainnya.
koljanep

Jawaban:

10

Dengan sedAnda dapat menggunakan rentang dan qmasukan input pada satu penyelesaian:

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

Demikian pula dengan GNU grepAnda dapat membagi input antara dua greps:

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

... yang mencetak ...

5:182
2:ABC

... untuk menandakan bahwa yang pertama grepmenemukan -Fstring ixed-string literal, -xseluruh baris 182 cocok 5 baris dari awal pembacaannya, dan yang kedua menemukan ABC match 2 baris yang sama dari awal readnya - atau 2 baris setelah yang pertama grep berhenti membaca di baris 5.

Dari 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. 

Saya menggunakan dokumen di sini demi demonstrasi yang dapat direproduksi, tetapi Anda mungkin harus melakukan:

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

Ini juga akan bekerja dengan konstruksi perintah majemuk shell lainnya seperti:

for p in 182 ABC; do grep -nxFm1 "$p"; done </path/to/log.file
mikeserv
sumber
+1 Melihatnya di halaman manual. Itulah yang saya coba, hanya dengan sebuah pipa di antara grep's bukannya ;... no-go
Xen2050
@ Xen2050 - pipa tidak akan berfungsi, biasanya - file lseekable biasanya adalah yang Anda inginkan saat berbagi input.
mikeserv
Jawaban yang mengesankan tetapi saya tidak mendukung pernyataan Anda tentang saluran pipa. Dokumen di sini yang grepdibagikan kedua orang tersebut secara efektif merupakan saluran pipa untuk mereka. Sesuatu yang lain: Saya mencoba tanpa mencetak garis penanda tetapi sed '//,/^ABC$/!d;/^ABC$/!d;q'melemparkan kesalahan yang aneh. Apa yang //harus dilakukan
Hauke ​​Laging
1
@ HaukeLaging - dokumen di sini (di sebagian besar shell) bukan pipa - itu adalah file tmp nyata yang dibuat oleh shell yang dihapus oleh shell sebelum melakukan penulisan apa pun - sambil mempertahankan deskriptor. Itu masih mabuk. Pipa, secara umum, tidak bisa disembuhkan. Saya akan melihat sedhal itu - hanya menuliskannya dengan sangat cepat.
mikeserv
1
@ HaukeLaging - Oh, jadi sedmasalahnya bekerja - Anda baru saja meninggalkan referensi. Di sedAnda dapat merujuk yang terakhir /address/lagi dengan //alamat kosong . Begitu /^182$/command;//,/next_address/juga /^182$/command;/^182$/,/next_address/. Kesalahan Anda mungkin bukan ekspresi reguler sebelumnya jika Anda menggunakan GNU sed. Ngomong-ngomong pipa lseek, omong-omong, dapat dimanipulasi melalui tipuan melalui /dev/fd/[num]tautan pada sistem linux - tetapi jika Anda tidak terlalu berhati-hati untuk menangani buffer dengan baik (seperti dengan dd) itu biasanya pertempuran yang kalah.
mikeserv
2

Gunakan grepdengan ekspresi reguler yang kompatibel dengan Perl ( pcregrep):

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

Opsi -Mmemungkinkan pola untuk mencocokkan lebih dari satu baris, dan \Ktidak termasuk pola yang cocok (hingga titik ini) ke dalam output. Anda dapat menghapus \Kjika Anda ingin memiliki seluruh wilayah sebagai hasilnya.

jimmij
sumber
2
> awk '/^182$/ { startline=1; }; startline == 0 { next; }; /^ABC$/ { print "line " NR ": " $0; exit; }' file
line 7: ABC
Hauke ​​Laging
sumber
1
Itu memberi ABC pertama di mana saja ; pertanyaan ini ingin ABC pertama setelah 182. Yang paling langsung adalah seperti flag awk '/^182$/{z=1;next} z&&/^ABC$/{print NR":"$0;exit}' file- atau Anda dapat menulis setidaknya satu getline()loop eksplisit yang biasanya clumsier, atau menjadi pintar (?) menggunakan rentang yang hampir seperti @ JRFerguson perl:awk '!x&&/^182$/,/^ABC$/ {x=NR":"$0} END{print x}
dave_thompson_085
@ dave_thompson_085 Memang. Ide yang benar tetapi sangat dikodekan (mencampurkan dua ide selama penulisan) Dengan malu saya bahkan mencoba tetapi tidak bertanya-tanya pada hasilnya.
Hauke ​​Laging
1

Variasi Perl yang dapat Anda gunakan adalah:

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

... yang mencetak garis dalam kisaran yang cocok.

Jika file Anda berisi lebih dari satu rentang yang cocok, Anda dapat membatasi output hanya rentang pertama dengan mengubah /pembatas menjadi?

perl -nle 'm?182?..?ABC? and print'
JRFerguson
sumber
1

Tetap dengan adil grepdan menambahkan tail& cut, Anda bisa ...

grep untuk nomor baris dari kecocokan pertama 182:

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

Menggunakannya untuk grep untuk semua ABC's hanya setelah baris pencocokan pertama di atas, dengan menggunakan tail' s -n +Kuntuk output setelah baris K'th. Bersama:

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

Atau tambahkan -m 1lagi untuk menemukan hanya pencocokan pertamaABC

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

Referensi:
manhalaman
/programming/6958841/use-grep-to-report-back-only-line-numbers

Xen2050
sumber
1

Varian lain adalah ini:

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

Bendera - Sebuah greps n baris setelah pertandingan dan 99999 hanya untuk memastikan kami tidak melewatkan apa pun. File yang lebih besar harus memiliki lebih banyak baris (tanyakan "wc -l").

Fabbe
sumber
0

Operator jangkauan ,dapat digunakan di sini:

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

Operator jangkauan ..bersama-sama dengan operator yang hanya cocok satu kali m??dapat digunakan di siniPerl

perl -lne 'm?182? .. m?ABC? and print "$.:$_" if /182/ || /ABC/' yourfile

sumber