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



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.


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


Terima kasih. Meskipun pada dasarnya ini adalah solusi yang sama, menginspirasi kepercayaan bahwa ini adalah metode terbaik ketika dua orang secara mandiri merekomendasikannya.
dotancohen


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.


0

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

grep "foobar" * | cut -b 1-400
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.