Batasi konteks grep hingga N karakter on line

31

Saya harus membaca beberapa file JSON yang panjangnya melebihi beberapa ribu karakter. Bagaimana saya bisa membatasi grep untuk menampilkan konteks hingga N karakter di kiri dan kanan pertandingan? Alat apa pun selain grep juga akan baik-baik saja, asalkan tersedia dalam paket Linux umum.

Ini akan menjadi contoh output, untuk sakelar grep imajiner Ф :

$ grep -r foo *
hello.txt: Once upon a time a big foo came out of the woods.

$ grep -Ф 10 -r foo *
hello.txt: ime a big foo came of t
dotancohen
sumber
3
stackoverflow.com/questions/8101701/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
1
Kemungkinan rangkap dari Cara menampilkan garis 2-4 setelah setiap hasil grep?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
3
Bukan duplikat. Ini sekitar ± karakter tetapi alternatif yang Anda sarankan adalah ± baris. (Referensi Anda ke stackoverflow bagus, meskipun.)
roaima

Jawaban:

22

Dengan GNU grep:

N=10; grep -roP ".{0,$N}foo.{0,$N}" .

Penjelasan:

  • -o => Cetak hanya apa yang Anda cocokkan
  • -P => Gunakan ekspresi reguler Perl-style
  • Regex mengatakan pertandingan 0 ke $Nkarakter diikuti oleh foodiikuti oleh 0 ke $Nkarakter.

Jika Anda tidak memiliki GNU grep:

find . -type f -exec \
    perl -nle '
        BEGIN{$N=10}
        print if s/^.*?(.{0,$N}foo.{0,$N}).*?$/$ARGV:$1/
    ' {} \;

Penjelasan:

Karena kita tidak bisa lagi mengandalkan grepmenjadi GNU grep, kami menggunakan finduntuk mencari file secara rekursif ( -rtindakan GNU grep). Untuk setiap file yang ditemukan, kami menjalankan cuplikan Perl.

Switch Perl:

  • -n Baca file baris demi baris
  • -l Hapus baris baru di akhir setiap baris dan pasang kembali saat mencetak
  • -e Perlakukan string berikut sebagai kode

Cuplikan Perl pada dasarnya melakukan hal yang sama dengan grep. Dimulai dengan mengatur variabel $Nke jumlah karakter konteks yang Anda inginkan. The BEGIN{}sarana ini dijalankan hanya sekali pada awal eksekusi tidak sekali untuk setiap baris dalam setiap file.

Pernyataan yang dieksekusi untuk setiap baris adalah untuk mencetak baris jika substitusi regex berfungsi.

Regex:

  • Cocokkan barang lama dengan malas 1 di awal baris ( ^.*?) diikuti oleh .{0,$N}seperti dalam grepkasus ini, foodiikuti oleh yang lain .{0,$N}dan akhirnya cocokkan barang lama dengan malas sampai akhir baris ( .*?$).
  • Kami menggantinya dengan $ARGV:$1. $ARGVadalah variabel magis yang menyimpan nama file saat ini sedang dibaca. $1adalah apa yang cocok dengan orangtua: konteks dalam kasus ini.
  • Pertandingan malas di kedua ujung diperlukan karena pertandingan serakah akan memakan semua karakter sebelum footanpa gagal untuk mencocokkan (karena .{0,$N}diizinkan untuk mencocokkan nol kali).

1 Artinya, lebih memilih untuk tidak mencocokkan apa pun kecuali ini akan menyebabkan kecocokan keseluruhan gagal. Singkatnya, sesuaikan karakter sesedikit mungkin.

Joseph R.
sumber
Sangat baik terima kasih. Ini memiliki kelemahan dalam menyorot seluruh output, tidak hanya teks yang dicari, tetapi juga dapat dikerjakan dengan menambahkan | grep foosampai akhir (namun kehilangan sorotan nama file dalam proses).
dotancohen
1
@dotancohen Saya kira Anda tidak bisa memenangkan semuanya :)
Joseph R.
w / GNU grepAnda dapat menentukan warna / aplikasi yang cocok berdasarkan flag yang diterapkan melalui variabel lingkungan. jadi mungkin bahkan Anda bisa memenangkan semuanya, (tidak ada janji - bahkan tidak yakin itu akan berhasil dalam kasus ini) tetapi saya pribadi tidak melihat relevansinya di sini ... tetap ... tetaplah bermain.
mikeserv
Jawaban bagus. Hanya sebuah catatan, menggunakan zshsaya tidak dapat membuatnya berfungsi melewati N = 10 seperti pada contoh. Namun itu berfungsi jika saya export N=10sebelum menjalankan perintah. Adakah yang tahu bagaimana menyesuaikan contoh agar berfungsi dengan zsh?
Gabe Kopley
Atauperl -lne 'print "$ARGV: $_" for /.{0,10}foo.{0,10}/g'
Stéphane Chazelas
20

Coba gunakan yang ini:

grep -r -E -o ".{0,10}wantedText.{0,10}" *

-E memberi tahu, bahwa Anda ingin menggunakan regex diperpanjang

-o memberi tahu, bahwa Anda hanya ingin mencetak pertandingan

-r grep sedang mencari hasil secara rekursif di folder

REGEX:

{0,10} memberi tahu, berapa banyak karakter sewenang-wenang yang ingin Anda cetak

. mewakili karakter yang berubah-ubah (karakter itu sendiri tidak penting di sini, hanya nomor mereka)

Sunting: Oh, begitu, Joseph merekomendasikan solusi yang hampir sama dengan saya: D

Eenoku
sumber
Terima kasih. Meskipun pada dasarnya ini adalah solusi yang sama, menginspirasi kepercayaan bahwa ini adalah metode terbaik ketika dua orang secara mandiri merekomendasikannya.
dotancohen
Sama-
2
Meskipun mereka mirip, jawaban yang diterima tidak bekerja untuk saya (masih menghasilkan antrian panjang), tetapi yang ini berhasil. Trik dengan N = 10 tidak bekerja dengan bash shell.
meesern
di cygwin -E secara signifikan lebih cepat daripada -P.
Bob Stein
2

Diambil dari: http://www.topbug.net/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserves-color/ dan https: // stackoverflow. com / a / 39029954/1150462

Pendekatan yang disarankan ".{0,10}<original pattern>.{0,10}"sangat baik kecuali untuk itu warna highlight sering kacau. Saya telah membuat skrip dengan output serupa tetapi warnanya juga dipertahankan:

#!/bin/bash

# Usage:
#   grepl PATTERN [FILE]

# how many characters around the searching keyword should be shown?
context_length=10

# What is the length of the control character for the color before and after the matching string?
# This is mostly determined by the environmental variable GREP_COLORS.
control_length_before=$(($(echo a | grep --color=always a | cut -d a -f '1' | wc -c)-1))
control_length_after=$(($(echo a | grep --color=always a | cut -d a -f '2' | wc -c)-1))

grep -E --color=always "$1" $2 | grep --color=none -oE ".{0,$(($control_length_before + $context_length))}$1.{0,$(($control_length_after + $context_length))}"

Dengan asumsi skrip disimpan sebagai grepl, maka grepl pattern file_with_long_linesharus menampilkan garis yang cocok tetapi dengan hanya 10 karakter di sekitar string yang cocok.

xuhdev
sumber
0

Stdout perpipaan cutdengan -bbendera; Anda dapat menginstruksikan output grep hanya byte 1 hingga 400 per baris.

grep "foobar" * | cut -b 1-400
Eric Leschinski
sumber