Ограничить вывод grep короткими строками

8

Я часто использую grep, чтобы найти файлы с определенной записью, подобной этой:

grep -R 'MyClassName'

Хорошо, что он возвращает файлы, их содержимое и помечает найденную строку красным цветом. Плохо то, что у меня также есть огромные файлы, где весь текст написан одной большой строкой. Теперь grep выводит слишком много при поиске текста в этих больших файлах. Есть ли способ ограничить вывод, например, 5 словами слева и справа? Или, может быть, ограничить вывод до 30 букв влево и вправо?

Сократ
источник
3
cut
Передайте
Итак, предположим, что шаблон, который вы ищете, находится в позиции 50, но вы сказали, что хотите только 30 букв. Что вы хотите сделать тогда? Игнорировать эту строку или также включить ее в вывод, но обрезать ее? Что именно вы хотите ограничить - поиск или сами строки?
Сергей Колодяжный
1
@Rinzwind Я не совсем понимаю, чего вы хотите достичь cut, так как он разделяется только по разделителю или количеству символов. Хотя, когда я нахожу строку с MyClassNameней, она может находиться где угодно в строке и не всегда в одной и той же позиции. Кроме того, могут быть различные символы спереди и сзади, что исключает возможность разделения по разделителю.
Сократ
1
@SergiyKolodyazhnyy Когда найдена положительная строка с MyClassName, я хочу получить в результате имя файла и символы x слева и справа. x - это любое число, которое я предоставляю, например, 30. Остальное содержимое файла должно игнорироваться. Это делается для того, чтобы получить контекст для соответствующих файлов и ограничить перегрузку.
Сократ
1
@Rinzwind Какой тип пользовательского разделителя вы бы предложили, cutесли есть три файла со следующим вводом: oiadfaosuoianavMyClassNameionaernaldfajdи /(/&%%§%/(§(/MyClassName&((/$/$/(§/$&и public class MyClassName { public static void main(String[] args) { } }?
Сократ,

Ответы:

15

grepсам по себе имеет только параметры для контекста, основанного на строках. В этом сообщении SU предлагается альтернатива :

Обходной путь - включить параметр «только сопоставление», а затем использовать возможности RegExp, чтобы получить немного больше, чем ваш текст:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}" ./filepath

Конечно, если вы используете цветовую подсветку, вы всегда можете использовать grep, чтобы закрасить только реальное соответствие:

grep -o ".\{0,50\}WHAT_I_M_SEARCHING.\{0,50\}"  ./filepath | grep "WHAT_I_M_SEARCHING"

В качестве другой альтернативы я бы предложил foldввести текст, а затем добавить его, например:

fold -sw 80 input.txt | grep ...

-sОпция сделает foldтолчок слова на следующую строку вместо разрыва между ними.

Или используйте другой способ разделения ввода по строкам в зависимости от структуры ввода. (Например, в сообщении SU речь шла о JSON, поэтому использование jqetc для красивой печати и grep... или просто использование jqсамой фильтрации ... было бы лучше, чем любая из двух альтернатив, приведенных выше.)


Этот метод GNU awk может быть быстрее:

gawk -v n=50 -v RS='MyClassName' '
  FNR > 1 { printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)}
  {p = substr($0, length - n); prt = RT}
' input.txt
  • Скажите awk разделить записи по интересующему нас шаблону ( -v RS=...) и количеству символов в контексте ( -v n=...)
  • Каждая запись после первой записи ( FNR > 1) - это запись, в которой awk находит соответствие шаблону.
  • Таким образом, мы печатаем nзавершающие символы из предыдущей строки ( p) и nначальные символы из текущей строки ( substr($0, 0, n)) вместе с соответствующим текстом для предыдущей строки (которая есть prt)
    • мы устанавливаем pи prt после печати, поэтому значение, которое мы устанавливаем, используется следующей строкой
    • RT это GNUism, поэтому это специфично для GNU awk.

Для рекурсивного поиска, возможно:

find . -type f -exec gawk -v n=50 -v RS='MyClassName' 'FNR>1{printf "%s: %s\n",FILENAME, p prt substr($0, 0, n)} {p = substr($0, length-n); prt = RT}' {} +
Мур
источник
2
Хорошо, это работает. Кажется, Regex - правильный подход, так что спасибо за это. Время обработки довольно большое, хотя. Без Regex, как в моем предыдущем посте, требуется 4,912 с, а с Regex, как в вашем посте, - 3 м 39,312 с.
Сократ
1
@Socrates посмотрим, работает ли метод awk, который я добавил выше, лучше
muru
1
foldМетод может быть использован только если вы уверены , что искомая строка не появляется на границе, в противном случае было бы получить скрыт grep.
Мелебиус
1
@muru Спасибо за ваше предложение с gawk. К сожалению, предложенная команда findвыводит случайные вещи без имен файлов, когда выполняется в моей системе. Кроме того, я недостаточно хорошо знаю, awkчтобы правильно проанализировать команду. В настоящее время Regex в сочетании с grepрешением проблемы может быть не быстрым, но надежным. Еще раз большое спасибо.
Сократ
1
@Socrates Думаю, мне удалось исправить команду awk. Моя ментальная модель была неверна в отношении того, какие строки, RTпрефиксы и т. Д. Должны были использоваться.
Муру
1

Использование только сопоставления в сочетании с некоторыми другими параметрами (см. Ниже) может быть очень близко к тому, что вы ищете, без затрат на обработку регулярных выражений, упомянутых в другом ответе.

grep -RnHo 'MyClassName'
  • n числовой вывод, показать номер строки совпадения
  • H filename, показать имя файла в начале строки совпадения
  • o только совпадения, показывать только строку, но не всю строку
Роберт Ридл
источник
Хотя это правда, что результат будет найден гораздо быстрее, информация отсутствует. Путь к файлу показан, номер строки показан, но вывод текста - это только мой начальный поиск MyClassName. Следовательно, контекст отсутствует.
Сократ
grep -RnHo "MyClassName"и grep -Rno "MyClassName"имеют одинаковый выход.
Сократ
Вывод @Socrates не тот же, без H в том же каталоге
Роберт Ридл
-oФлаг может быть интересен , если регулярное выражение было некоторая переменная часть. Для фиксированной строки бесполезно печатать ее каждый раз. ОП скорее всего интересует ближний контекст.
Мелебиус
1
@ Сократ, правда - контекст отсутствует, но я подумал, в чем суть? Ограничить выход? Вы можете добавить контекст снова, добавив строки до ( -B 1) или после ( -A 1). Извините, что я не мог помочь.
Роберт Ридл