Temukan semua kejadian dalam file dengan sed

15

Menggunakan OPEN STEP 4.2 OS ... Saat ini saya menggunakan sedPerintah berikut :

sed -n '1,/141.299.99.1/p' TESTFILE | tail -3

Perintah ini akan menemukan satu instance dalam file dengan ip 141.299.99.1 dan juga menyertakan 3 baris sebelumnya yang semuanya baik, dengan pengecualian bahwa saya juga ingin menemukan semua instance IP dan 3 baris sebelumnya dan bukan hanya yang pertama.

Lembah
sumber
1
Harap selalu sertakan OS Anda. Solusi sangat sering bergantung pada Sistem Operasi yang digunakan. Apakah Anda menggunakan Unix, Linux, BSD, OSX, sesuatu yang lain? Versi yang mana?
terdon
TITIK HEBAT! Menggunakan Open Step versi 4.2 sudah cukup lama dan shell yang disertakan tidak menyertakan banyak fitur yang disebutkan dalam jawaban di bawah ini.
Dale
Karena penasaran - apa itu sistem OPEN STEP 4.2 dan apa yang digunakan untuk hari ini?
Thorbjørn Ravn Andersen
(dan jika Perl tersedia Anda benar-benar dapat melakukan banyak hal baik hanya dengan itu)
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Mungkin ini: en.wikipedia.org/wiki/OpenStep
Barmar

Jawaban:

4

Berikut adalah upaya untuk meniru grep -B3menggunakan sed moving window, berdasarkan contoh sed GNU ini (tapi semoga sesuai dengan POSIX - dengan pengakuan ke @ StéphaneChazelas):

sed -e '1h;2,4{;H;g;}' -e '1,3d' -e '/141\.299\.99\.1/P' -e '$!N;D' file

Dua ekspresi pertama prime buffer pola multi-line dan memungkinkannya untuk menangani kasus tepi di mana ada kurang dari 3 baris konteks sebelumnya sebelum pertandingan pertama. Ekspresi tengah (pencocokan regex) mencetak garis dari atas jendela sampai teks pencocokan yang diinginkan telah berdesir melalui buffer pola. Final $!N;Dmenggulung jendela dengan satu baris kecuali ketika mencapai akhir input.

Steeldriver
sumber
-etidak spesifik GNU. Untuk menjadi POSIX / portable, Anda memang membutuhkannya karena tidak ada apa pun setelahnya }(dan Anda memerlukannya ;sebelum itu).
Stéphane Chazelas
Terima kasih @ StéphaneChazelas - jadi apakah Anda mengatakan bahwa untuk menjadi POSIX / portable, kelompok pertama perlu dipecah / dimodifikasi -e '1h;2,4{H;g;}' -e '1,3d'? Saya tidak memiliki sistem non-GNU untuk diuji (dan --posixsaklar sed GNU tampaknya tidak peduli).
steeldriver
1
Ya, di Linux, Anda dapat menguji implementasi yang berbeda dengan seddari heirloom toolchest yang merupakan turunan dari perangkat Unix tradisional. POSIX / Unix spec untuk seddi pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
Stéphane Chazelas
Saya mendapatkan acara tidak ditemukan di salah satu dari ini: N; D ': Acara tidak ditemukan. Apakah saya kehilangan sintaksis di suatu tempat? Terima kasih!!
Dale
Maaf saya baru menyadari bahwa suntingan terbaru saya menghilangkan satu kutipan penutup setelah ekspresi -e pertama. Saya telah memperbaikinya sekarang - dapatkah Anda mencoba lagi dengan ungkapan di atas?
steeldriver
10

grep akan melakukan pekerjaan ini dengan lebih baik:

grep -B 3 141.299.99.1 TESTFILE

The -B 3sarana untuk mencetak tiga baris sebelum setiap pertandingan. Ini akan mencetak di --antara setiap kelompok garis. Untuk menonaktifkannya, gunakan --no-group-separatorjuga.

The -Boption didukung oleh GNUgrep dan sebagian besar versi BSD juga ( OSX , FreeBSD , OpenBSD , NetBSD ), tapi secara teknis bukan pilihan standar.

Michael Homer
sumber
1
Michael Homer - Terima kasih. Saya tidak punya opsi - B. Ada ide lain?
Dale
@Dale Bisakah Anda menginstal GNU grep? Itu akan memberi Anda pilihan.
Barmar
9

Dengan sedAnda bisa melakukan sliding window.

sed '1N;$!N;/141.299.99.1/P;D'

Itu berhasil. Tapi waspadalah - bashperilaku gila ekspansi ! bahkan ketika dikutip !!! ke dalam string perintah dari sejarah perintah Anda mungkin membuatnya sedikit gila. Awali perintah dengan set +H;jika Anda menemukan ini masalahnya. Untuk kemudian mengaktifkannya kembali (tapi mengapa ???) lakukan set -Hsesudahnya.

Itu, tentu saja, hanya akan berlaku jika Anda sedang menggunakan bash- meskipun saya tidak percaya Anda. Saya cukup yakin Anda bekerja dengan csh- (yang kebetulan shell yang perilaku gila bashditiru dengan ekspansi sejarah, tapi mungkin tidak terlalu ekstrem shell c mengambilnya) . Jadi mungkin suatu \!harus bekerja. Saya harap.

Ini semua kode portabel: POSIX menjelaskan tiga operatornya sebagai berikut: (meskipun perlu dicatat bahwa saya hanya mengonfirmasi deskripsi ini sudah ada sejak tahun 2001)

[2addr]N Tambahkan baris input berikutnya, kurang garis \nputusnya, ke ruang pola, menggunakan garis \ntepi tertanam untuk memisahkan bahan yang ditambahkan dari bahan asli. Perhatikan bahwa nomor baris saat ini berubah.

[2addr]P Tulis ruang pola, hingga baris pertama \n, ke output standar.

[2addr]D Hapus segmen awal ruang pola melalui baris pertama \ndan mulai siklus berikutnya.

Jadi pada baris pertama Anda menambahkan garis ekstra ke ruang pola, sehingga terlihat seperti ini:

^line 1s contents\nline 2s contents$

Kemudian pada baris pertama dan setiap baris sesudahnya - kecuali yang terakhir - Anda menambahkan baris lain ke ruang pola. Jadi sepertinya ini:

^line 1\nline 2\nline 3$

Jika alamat ip Anda ditemukan di dalam diri Anda Phingga baris baru pertama, maka cukup baris 1 di sini. Di akhir setiap siklus, Anda Dmengelakan hal yang sama dan memulai kembali dengan yang tersisa. Jadi siklus selanjutnya terlihat seperti:

^line 2\nline 3\nline 4$

...dan seterusnya. Jika ip Anda dapat ditemukan pada salah satu dari tiga yang tertua akan dicetak - setiap saat. Jadi, Anda selalu hanya tiga baris di depan.

Ini contoh singkatnya. Saya akan mencetak buffer tiga baris untuk setiap angka yang diakhiri dengan nol:

seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'

10
18
19
20
28
29
30
38
39
40
48
49
50

Yang itu sedikit lebih rumit daripada kasus Anda karena saya harus berganti dari 0\nbaris baru atau0$ akhir pola ruang untuk lebih mirip masalah Anda - tetapi mereka agak berbeda dalam hal ini membutuhkan jangkar - yang dapat sedikit sulit dilakukan karena pola-ruang terus bergeser.

Saya menggunakan kasus aneh 10 dan 52 untuk menunjukkan bahwa selama jangkar fleksibel maka outputnya juga. Sepenuhnya mudah dibawa, saya dapat mencapai hasil yang sama dengan mengandalkan algoritma dan melakukan:

seq 10 52 | sed '1N;$!N;/[90]\n/P;D'

Dan memperluas pencarian sambil membatasi jendela saya - dari 0 hingga 9 dan 0 dan dari 3 baris menjadi dua.

Bagaimanapun, Anda mendapatkan idenya.

mikeserv
sumber
Terima kasih untuk semua kerja kerasmu. Maaf, di mana saya akan meletakkan nama file yang ingin saya cari?
Dale
@Dale - salahku. sed '...' $filename. By the way - saya meninggalkan periode dari string pencarian Anda sendiri, tetapi itu sebenarnya bukan periode dalam suatu pola - yang mewakili karakter tunggal. Anda mungkin harus melakukannya oct\.oct\.oct\.octuntuk melarikan diri sehingga mereka hanya cocok dengan periode.
mikeserv
Saya mencoba untuk menggunakannya dan simbol <> yang berbeda dan saya mendapatkan acara tidak ditemukan yang saya dapatkan dengan solusi lain di sini jadi saya bertanya-tanya apakah OS saya tidak kompatibel dengan solusi ini.
Dale
sekarang hasil dengan -> N; /141.299.99.1/P; D ': Peristiwa tidak ditemukan.
Dale
@Dale - silakan lihat pembaruan. Itu akan membantu Anda.
mikeserv
4

Karena Anda menyebutkan bahwa Anda tidak memiliki -Bopsi untuk grep, Anda dapat menggunakan Perl (misalnya) untuk membuat jendela geser 4 baris:

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

Jawaban Ramesh melakukan hal serupa dengan awk.

Joseph R.
sumber
Saya tidak yakin apakah versi Perl saya mendukung ini, tetapi saya akan mencobanya. Terima kasih banyak telah meluangkan waktu untuk menjawab pertanyaan saya - sangat berterima kasih!
Dale
@Dale Terima kasih banyak. Saya ragu bahwa kode ini menggunakan fitur Perl mutakhir.
Joseph R.
4

Jika tersedia, Anda dapat menggunakan pcregrep :

pcregrep -M '.*\n.*\n.*\n141.299.99.1' file
kekacauan
sumber
Memeriksa apakah saya memiliki PCREGREP. Saya suka kekompakan perintah. Sangat berterima kasih atas waktu dan usaha Anda. Terima kasih!!!
Dale
4

Anda dapat menerapkan pendekatan dasar yang sama dengan jawaban non-grep di shell itu sendiri (ini mengasumsikan shell yang relatif baru yang mendukung =~):

while IFS= read -r line; do 
    [[ $line =~ 141.299.99.1 ]] && printf "%s\n%s\n%s\n%s\n" $a $b $c $line;
    a=$b; b=$c; c=$line; 
done < file 

Atau, Anda bisa menghirup seluruh file menjadi array:

perl -e '@F=<>; 
        for($i=0;$i<=$#F;$i++){
          print $F[$i-3],$F[$i-2],$F[$i-1],$F[$i] if $F[$i]=~/141.299.99.1/
        }' file 
terdon
sumber
Cangkang saya sudah sangat tua - Steve Jobs Open Step. Ide bagus dan terima kasih atas waktu Anda !!! Dale
Dale
@Dale pendekatan perl akan bekerja di mana saja. Beri tahu kami sistem operasi Anda (tambahkan ke pertanyaan Anda) dengan cara itu kami dapat menyarankan hal-hal yang akan bekerja untuk Anda.
terdon
Jika saya menyalin Perl Anda dan meletakkannya di NotePad dan meletakkannya di satu baris itu berfungsi! Pertanyaan - jika saya ingin katakan 10 baris sebelum pola pertandingan, di mana saya akan mengubah 3 menjadi 10? Terima kasih!
Dale
Saya melihat bahwa saya dapat menambahkan lebih banyak baris kembali dengan menambahkan lebih banyak $ F [$ iX], pernyataan. Terima kasih!
Dale
4

Jika sistem Anda tidak mendukung grepkonteks, Anda dapat mencoba ack-grep sebagai gantinya:

ack -B 3 141.299.99.1 file

ack adalah alat seperti grep, dioptimalkan untuk programmer.

cuonglm
sumber
Saya suka kekompakan perintah tetapi sistem saya tidak mendukung ACK dalam mencari di halaman manual. Ide bagus dan terima kasih banyak atas waktu Anda !!! Dale
Dale
@Dale: Mengejutkan! Apa OS kamu? Jika sudah perl, Anda bisa menggunakan ack.
cuonglm
2
awk '/141.299.99.1/{for(i=1;i<=x;)print a[i++];print} {for(i=1;i<x;i++)
     a[i]=a[i+1];a[x]=$0;}'  x=3 filename

Dalam awksolusi ini , array digunakan yang akan selalu berisi 3 baris sebelum pola saat ini. Oleh karena itu, ketika pola dicocokkan, isi array bersama dengan pola saat ini dicetak.

Pengujian

-bash-3.2$ cat filename
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
10.0.0.17
10.0.0.18
10.0.0.19

Setelah saya menjalankan perintah, hasilnya adalah,

10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
Ramesh
sumber
sangat detail - terima kasih banyak. Saya akan mencobanya. Sangat berterima kasih atas waktu Anda !! Dale
Dale
Saya punya file uji dan solusi Anda berfungsi! Masalahnya adalah ketika saya menjalankannya pada file produksi besar saya, ia kembali dengan Nomor Rekaman Terlalu Panjang sehingga output tidak dapat bekerja dengan perintah. Perintah asli saya di bagian atas halaman ini berfungsi tetapi hanya menemukan satu contoh. Saya menghargai bantuan Anda. Apakah ada yang bisa saya lakukan dengan perintah asli saya untuk membuatnya menemukan lebih dari satu instatnce?
Dale
1

Dalam sebagian besar dari ini, /141.299.99.1/juga akan cocok (misalnya) 141a299q99+1atau 141029969951karena. dalam ekspresi reguler dapat mewakili karakter apa pun.

Menggunakan /141[.]299[.]99[.]1/lebih aman, dan Anda dapat menambahkan konteks tambahan di awal dan akhir dari seluruh regexp untuk memastikan tidak cocok 3141., .12, .104, dll

pengguna117529
sumber
1
Ini adalah poin yang bagus - dan saya juga mempertimbangkannya. Namun, saya menggunakan string yang disediakan oleh penanya sebagai pertandingan yang dikenal - dan memberi tahu dia secara pribadi tentang hal yang sama ketika memberikan kesempatan. Pokoknya - tidak semua ini - jawaban steeldriver telah mengutip pertandingan char dari awal.
mikeserv