Apakah ada alternatif untuk switch grep -A -B -C (untuk mencetak beberapa baris sebelum dan sesudah)?


10
grep -A 2 -B 3 

mencetak 2 baris setelah grep string dan mencetak 3 baris sebelumnya.

grep -C 3

mencetak 3 Garis sebelum dan 3 garis sesudahnya

Sayangnya, grepsaya menggunakan tidak mendukung opsi ini. Apakah ada perintah atau skrip alternatif yang tersedia untuk mensimulasikan ini? Menggunakan sed/ awk/ perl/ skrip shell?


+1 Saya tidak tahu ada -Csaklar.
Lazer

1
Instal grep GNU. Lebih umum, ketika mesin Sun baru tiba di sini, langkah pertama dalam pengaturan adalah apa yang disebut seseorang GNU > /usr/local. Program GNU memiliki banyak ekstensi yang sangat berguna, dan dirancang untuk menghindari pembatasan sewenang-wenang (tetapi Anda membayar dalam ukuran besar dan kadang-kadang kinerja). Banyak sistem propietary memiliki repositori paket "tidak resmi" dengan GNU dan alat-alat lainnya. "Mitra" tidak akan memberi tahu Anda tentang mereka, bahkan ketika mereka dikelola oleh vendor ...
vonbrand

Jawaban:


6

Salah satu cara jelek untuk melakukannya adalah

grep -v pattern file >file.tmp; diff -c file.tmp file

atau ganti -cdengan -C NUMuntuk NUMgaris konteks. Ini akan menghasilkan output ekstra. (Jika Anda diffmendukung -u/ -U NUM, itu akan lebih bersih.)

Jika Anda difftidak memiliki -c/ -C/ -u, masih ada cara untuk melakukannya, tetapi mereka sangat jelek. Di sisi lain, sistem yang diffbahkan tidak mendukung -cmungkin juga tidak memiliki Perl.


Ini keren, Bekerja seperti pesona, meskipun saya harus menggunakan opsi -bitw dengan ini untuk membuatnya berfungsi untuk file yang dihasilkan windows.
Prashant Bhate

Anda dapat mengirim stdin ke diff, dan melewatkan sementara:grep -v pattern file | diff -c - file
Cascabel

5

Jack hanya membutuhkan Perl, dan termasuk -A, -Bdan -Cpilihan yang bekerja seperti grep ini. Ini menggunakan sintaks regex Perl bukan grep, dan cara memilih file untuk pencarian sangat berbeda. Anda mungkin ingin mencoba -fopsi ketika menggunakannya (yang mencetak file yang akan dicari tanpa benar-benar mencari apa pun).

Itu dapat diinstal sebagai skrip tunggal yang tidak memerlukan modul non-inti. Cukup taruh di ~/bindirektori Anda (atau di mana pun di PATH Anda bahwa Anda memiliki akses tulis) dan pastikan itu chmoddieksekusi.


Kotak produksinya dan Sayangnya saya tidak memiliki cukup hak untuk menginstal apa pun, dan saya tidak bisa mengambil risiko, meskipun, terima kasih atas tip ini saya akan menginstalnya dan mencoba laptop rumah saya
Prashant Bhate

@Pashash, Anda tidak perlu menginstal root untuk ackdigunakan sendiri.
cjm

Ya tapi tetap saya tidak bisa menggunakannya di sana, meskipun yakin bahwa skrip ini akan tetap selamanya di ~ / bin saya :)
Prashant Bhate

@Pashash: Mengapa Anda tidak bisa menggunakannya? Itu hanya skrip perl.
intuited

1
Kotak PRODUKSI, perlu mengambil persetujuan izin khusus bla bla ... untuk melakukan hal di atasnya. dan ada yang tidak beres di sana datang di kepalaku;) dan itu tidak layak :)
Prashant Bhate

5

Skrip perl sederhana ini ditiru grep -Ahingga batas tertentu

#!/usr/bin/perl

$pattern=shift; #patthern to search
$lines=shift; # number of lines to print

$n = 0;
while (<>) {
  $n = $lines if /$pattern/; # reset counting
  if ($n) { print; $n-- } # print if within
  $n = 0 if eof; # don't leak across file boundaries
}

Perhatikan bahwa Anda dapat menambahkan pernyataan penggunaan, agar skrip dapat dibaca dan digunakan;)

USAGE:    $./grep-A.pl <pattern> <numLines> <filename> 

Bagus, perl versi apa yang saya perlukan untuk menjalankan ini?
Prashant Bhate

Saya menggunakan v5.10.1, saya kira perl 5 cukup umum hari ini.
Vijay Anant

ya 5.8.8 dan itu berfungsi, bagus, tapi saya perlu skrip yang melakukan apa -B lakukan
Prashant Bhate

Baik. Saya akan mengubah urutan argumen; grep-A 3 footerlihat jauh lebih alami daripada grep-A foo 3. :-)
musiphil

3

Anda cukup menginstal GNU grep atau Ack (ditulis dalam Perl, memahami banyak opsi GNU grep dan banyak lagi).

Jika Anda lebih suka tetap menggunakan alat standar ditambah sedikit skrip, berikut ini adalah skrip awk yang mengemulasi perilaku GNU grep -Adan -Bopsi. Diuji minimal.

#!/bin/sh
# grep-ac: a grep-like awk script
# Arguments: pattern = awk regexp to search for
#            before = number of lines to print before a match
#            after = number of lines to print after a match
{ "exec" "awk" "-f" "$0" "$@"; }
# The array h contains the history of lines that haven't been printed
# but are eligible for being "before" lines.
# The variable until contains the number of the last "after" line to print.
match($0, pattern) {   # the current line matches
    for (i in h) {
        print h[i];    # print each remaining before line
        delete h[i];   # delete each line as it's printed
    }
    until=NR+after;    # record the last after line to print
}
{
    if (NR<=until) print $0;    # from a match to its last after line: print
    else h[NR]=$0;              # after that: save in history
    delete h[NR-before];        # remove line too old to be a before line
}
END {exit !until}               # exit status: 0 if there was a match, else 1

Jalankan sebagai di grep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTERmana PATTERNpola untuk mencari ( ekspresi reguler yang diperluas dengan beberapa penambahan awk ), dan NBEFOREdan NAFTERadalah jumlah baris untuk dicetak masing-masing sebelum dan setelah pertandingan (default ke 0). Contoh:

<input_file grep-ac -vbefore=2 -vpattern='foo *bar'

Solusi apa pun yang menyimpan data dalam array tidak mungkin ... seperti yang saya sebutkan sebelumnya, ukuran file cukup besar dan mungkin overflow. Juga awk pada sistem ini tidak memungkinkan ukuran file lebih dari 3000 byte.
Prashant Bhate

2
@Pashash: Saya tidak mengerti keberatan Anda. Script ini menghapus baris begitu mereka tidak memenuhi syarat untuk menjadi sebelum-baris. Itu tidak menggunakan lebih banyak memori daripada yang secara inheren perlu diberikan persyaratan, kecuali bahwa awk mungkin memiliki overhead yang lebih tinggi daripada program tujuan khusus (tetapi kurang dari Perl, yang juga Anda pertimbangkan). Ukuran total file sama sekali tidak relevan.
Gilles 'SO- berhenti bersikap jahat'

2
{ "exec" "awk" "-f" "$0" "$@"; }: cara yang sangat bagus untuk menyiasati keterbatasan dalam parsing garis shebang.
dubiousjim

2

Ternyata cukup sulit untuk meniru -B, karena masalah yang muncul ketika Anda memiliki garis yang cocok mengikuti satu sama lain secara langsung. Ini cukup banyak melarang menggunakan segala jenis pemindaian file pass-through tunggal.

Saya menyadari hal ini saat bermain-main dengan perkiraan berikut:

perl -pe 'if(/search_term/) {print foreach @A; print ">"; $B=4}; shift @A if push(@A, $_)>7; $_ = "" unless ($B-- > 0);' target_file

Ini akan bekerja kira-kira dengan benar seperti grep -A7 -B3, dengan peringatan yang dijelaskan dalam paragraf pertama.

Solusi alternatif (juga file tunggal) untuk masalah ini adalah dengan menggunakan perl untuk memberi makan melalui string perintah:

sed -n `perl -pe '$_=(/search_term/?sprintf("%d,%dp;", $.-3,$.+4):"")' file` file

oneliner cukup lenghty, tapi, file ini sangat besar, jadi mendorong baris ke array dalam kasus ini adalah Ide yang buruk, bukan?
Prashant Bhate

The shift @A if push(@A,$_)>7;bit hanya membuat sebuah array dari ukuran maksimum 7 sekitar. (Itu -A parameter Anda). Pilihan kedua menyimpan file yang sangat kecil di sekitar (jalankan perl tanpa lapisan luar sed untuk melihat apa yang dihasilkan di sana), tetapi itu membaca file dua kali.
user455

0

Menggunakan sedAnda pertama-tama bisa mendapatkan nomor baris dari garis yang cocok, mengurangi & menambah nomor baris yang diberikan dalam satu whilelingkaran dan kemudian gunakan sed -n "n1,n2p"untuk mencetak garis konteks leading ( n1) dan trailing ( n2) (mirip dengan sedalternatif yang disarankan oleh pengguna455). Banyak proses membaca dapat menyebabkan hit kinerja.

eddapat secara langsung mereferensikan garis sebelumnya dan berikut dari garis yang cocok, tetapi gagal jika rentang garis yang ditentukan tidak ada; misalnya, baris yang cocok adalah baris nomor 2, tetapi 5 baris pra-pertandingan harus dicetak. Menggunakan edkarena itu perlu untuk menambahkan jumlah yang sesuai baris (kosong) pada awal dan akhir. (Untuk file besar edmungkin bukan alat yang tepat, lihat: bfs - pemindai file besar ).

# sample code to match lines with number 5 plus previous & following line
# (using Bash)
printf '%s\n' {1..20} > num.txt

# sed
sed -n '/5/=' num.txt | while read num; do
   n1=$((num - 1))
   n2=$((num + 1))
   [[ $n1 -lt 1 ]] && n1=1
   sed -n "${n1},${n2}p" num.txt
   echo --
done | sed -e '${/^--$/d;}'

# ed
cat <<-'EOF' | ed -s num.txt | sed -e $'N;N;a\\\n--' | sed -e '${/^--$/d;}'
H
0i
beginning: added line one
.
$a
end: added line one
.
,g/5/km\
'm-1,'m+1p
q
EOF
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.