Обмежте контекст греппінгу лише N символами


31

Мені потрібно проглядати деякі файли JSON, у яких довжина рядків перевищує кілька тисяч символів. Як я можу обмежити grep для відображення контексту до N символів зліва та справа від матчу? Будь-який інструмент, крім грепу, також буде добре, доки він доступний у загальних пакетах Linux.

Це був би приклад виводу для уявного перемикача греп Ф :

$ 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
Не дублікат. Це приблизно ± символів, але запропонована альтернатива - приблизно ± рядки. ( Хоча ваша посилання на stackoverflow хороша.)
roaima

Відповіді:


22

З GNU grep:

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

Пояснення:

  • -o => Роздрукуйте лише те, що ви відповідали
  • -P => Використовуйте регулярні вирази у стилі Perl
  • У регулярному вираженні $Nйде відповідність символів 0, а fooпотім 0 $Nсимволів.

Якщо у вас немає GNU grep:

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

Пояснення:

Оскільки ми більше не можемо покладатися на grepGNU grep, ми використовуємо findдля пошуку файлів рекурсивно ( -rдія GNU grep). Для кожного знайденого файлу ми виконуємо фрагмент Perl.

Перемикачі Perl:

  • -n Прочитайте файл за рядком
  • -l Видаліть новий рядок в кінці кожного рядка і поставте його назад під час друку
  • -e Розгляньте наступний рядок як код

Фрагмент Perl робить по суті те саме, що і grep. Він починається з встановлення змінної $Nна кількість потрібних символів контексту. Засіб BEGIN{}це виконується лише один раз на початку виконання не один раз для кожного рядка кожного файлу.

Оператор, виконаний для кожного рядка, - це надрукувати рядок, якщо заміна регулярного вираження працює.

Регекс:

  • Ліниво поєднуйте будь-яку стару річ 1 на початку, ^.*?а потім, .{0,$N}як у grepвипадку, за нею fooслідує інша .{0,$N}і, нарешті, ліниво до кінця рядка ( .*?$).
  • Ми замінюємо це на $ARGV:$1. $ARGV- це магічна змінна, яка містить ім'я поточного файла, який читається. $1це те, що відповідає паренам: контекст у даному випадку.
  • Ліниві сірники в будь-якому кінці потрібні, тому що жадібний матч з'їдав би всіх персонажів раніше, fooне збігаючись (оскільки .{0,$N}дозволено збігатися в нуль разів).

1 Тобто, волійте нічого не порівнювати, якщо це не спричинить збій загальної відповідності. Коротше кажучи, підберіть якомога менше символів.


Дуже приємно, дякую. Це має недолік виділення всього виводу, не лише шуканого тексту, але його можна вирішити, додавши | grep fooйого до кінця (однак втратити виділення імені файлу в процесі).
dotancohen

1
@dotancohen Я думаю, ви не зможете перемогти їх усіх :)
Джозеф Р.

w / GNU grepви можете вказати кольори / програми відповідності на основі прапорів, застосованих через змінні середовища. тож, можливо, ви навіть можете виграти їх усіх (жодних обіцянок - навіть не впевнених, що це спрацює в цьому випадку), але я особисто не бачу актуальності тут ... все одно ... продовжуйте грати.
mikeserv

Гарна відповідь. Просто зауваження, використовуючи zshя не в змозі змусити його працювати, переходячи N = 10, як у прикладі. Однак це спрацьовує, якщо я export N=10до запуску команди. Будь-яка ідея, як налаштувати приклад для роботи з zsh?
Гейб Коплей

Абоperl -lne 'print "$ARGV: $_" for /.{0,10}foo.{0,10}/g'
Стефан Шазелас

20

Спробуйте скористатися цим:

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

-E повідомляє, що ви хочете використовувати розширений регулярний вираз

-o каже, що ви хочете надрукувати лише відповідність

-r grep шукає результат рекурсивно в папці

REGEX:

{0,10} повідомляє, скільки довільних символів ви хочете надрукувати

. являє собою довільний характер (сам персонаж тут був не важливий, просто їх кількість)

Редагувати: О, я бачу, що Йосип рекомендує майже те саме рішення, що і я: D


Дякую. Незважаючи на те, що це по суті одне і те саме рішення, вселяє впевненість, що це найкращий метод, коли двоє людей самостійно рекомендують його.
dotancohen

Запрошуємо вас, спільнота Unix просто мусить співпрацювати, ось що ми :-)
Eenoku

2
Хоча вони схожі, прийнята відповідь не спрацювала для мене (все ще випускала довгі рядки), але одна це зробила. Трюк з N = 10 не працює з bash shell.
meesern

у цигвіна -E значно швидше, ніж -P.
Боб Stein

2

Взято з: http://www.topbug.net/blog/2016/08/18/truncate-long-matching-lines-of-grep-a-solution-that-preserve-color/ та https: // stackoverflow. com / a / 39029954/1150462

Запропонований підхід ".{0,10}<original pattern>.{0,10}"ідеально хороший, за винятком того, що колір виділення часто псується. Я створив сценарій з подібним результатом, але колір також збережений:

#!/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))}"

Якщо припустимо, що сценарій збережено як grepl, тоді він grepl pattern file_with_long_linesповинен відображати відповідні рядки, але лише 10 символів навколо відповідного рядка.


0

Трубопровід по cutлінії -bпрапора; ви можете вказувати висновку grep лише в байтах від 1 до 400 на рядок.

grep "foobar" * | cut -b 1-400
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.