grep для возврата N-й и M-й строк до и после матча


12

Я знаю, что с помощью grep я могу использовать поля -Aи -Bизвлекать предыдущую и следующую строки из совпадения.

Тем не менее, они вытягивают все строки между совпадениями, основываясь на том, сколько строк указано.

grep -r -i -B 5 -A 5 "match" 

Я хотел бы получить только 5- ю строку перед матчем и 5- ю строку после матча в дополнение к совпадающей строке, а не получить линии между ними.

Есть ли способ сделать это с grep?


1
Вы можете сделать это, отправив его в sed. Я только что проверил это, и это сработало, но оно сработало только тогда, когда в файле было 1 точное совпадение: grep -r -i -B 5 -A 5 "match" | sed -e 1b -e '$!d'
Terrance

@ Терренс спасибо за предложение, как вы упомянули, так как я собираю тысячи строк, это не сработает.
Холлида

Я не думаю, что grep будет работать сам по себе ... Я работаю над сценарием bash для вас
Джошуа Беснеатте

Нет проблем! Вроде бы интересно посмотреть какие ответы вы получите. =)
Терренс

это в одном файле или в нескольких файлах?
Джошуа Беснеатте

Ответы:


1

Инструмент, который вы хотите использовать, называется sift. Это в основном grep на стероидах. Grep параллельно. У Sift есть огромное количество опций для того, чтобы делать именно то, что вы хотите - в частности, возвращать определенную строку относительно соответствия (ей), за которым может / не может следовать / предшествовать какой-либо текст.

Меня удивляет, что sift не является основным gnu, как он написан на языке go, но отлично устанавливается на Linux. ИТ выполняет параллельный поиск, используя весь объем огромного количества текста, где grep требуется всего несколько недель, чтобы сделать то же самое.

Сайт просеивания - см. Примеры


Добро пожаловать в AskUbuntu, спасибо за ответ. Вам нужно предоставить пример CLI, который может решить эту конкретную проблему, а не предоставить ссылку на просеивающий сайт. Это вопрос и ответ, спасибо.
Бернард Вей

12

Если:

cat file
a
b
c
d
e
f match
g
h
i match
j
k
l
m
n
o

Потом:

awk '
    {line[NR] = $0} 
    /match/ {matched[NR]} 
    END {
        for (nr in matched)
            for (n=nr-5; n<=nr+5; n+=5) 
                print line[n]
    }
' file
a
f match
k
d
i match
n

+1, а не могли бы вы объяснить семантику /match/ {matched[NR]}? Я никогда не видел массив или переменную как целую команду. Помещает ли это номер текущей записи каждой совпавшей строки в массив.
Джо

Это странная вещь: если вы ссылаетесь на элемент массива без присваивания, этот ключ добавляется в массив (без значения). Затем этот ключ появляется в выражении key in array. Я запоминаю номера строк, где появляется шаблон
Гленн Джекман

6

Это в основном решение Гленна, но реализовано с помощью Bash, Grep и sed.

grep -n match file |
    while IFS=: read nr _; do
        sed -ns "$((nr-5))p; $((nr))p; $((nr+5))p" file
    done

Обратите внимание, что номера строк меньше 1 приведут к ошибке sed, а номера строк, превышающие количество строк в файле, не приведут к выводу ничего.

Это просто минимум. Чтобы заставить его работать рекурсивно и обрабатывать описанные выше случаи с номерами строк, потребуется некоторое время.


6

Это не может быть сделано только с grep. Если edвариант:

ed -s file << 'EOF' 
g/match/-5p\
+5p\
+5p
EOF  

Сценарий в основном говорит: для каждого соответствия / match / выведите строку 5 строк до этого, затем 5 строк после этого, затем 5 строк после этого.


5
@ubashu Как вы думаете, будет ли полезнее для ОП дать простую квартиру "это не может быть сделано с помощью grep"? Я предоставляю то, что я считаю хорошей альтернативой для решения проблемы ОП. Из справочного центра: «Что, в частности, задает вопрос? Убедитесь, что ваш ответ дает это - или является приемлемой альтернативой. Ответ может быть« не делай этого », но он также должен включать« попробуй вместо этого » «.
JoL

edэто всегда ответ, потому что edэто стандартный текстовый редактор.
десерт

5
@ubashu Хотя это и не grepответ, ответ «Вы не можете сделать это с помощью X, но вы можете сделать это с помощью Y, вот как» все еще является верным ответом, поскольку вы не только отвечаете на вопрос OP, но и предоставляете альтернативу это будет работать. Это правильный тип ответа здесь.
Томас Уорд

5
awk '/match/{system("sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME)}' infile

Здесь мы используем AWK «s функцию для вызова внешней команды для печати линии , которые AWK совпавшие с рисунком с 5 - й линии до и после матча.system(command)sedmatch

Синтаксис прост, вам просто нужно поместить внешнюю команду внутри двойной кавычки, а также ее переключатели и экранировать то, что вы хотите точно передать команде, все остальное, относящееся к параметрам awkсамой себя, должно быть вне кавычек. Итак, нижеприведенный сед :

"sed -n \"" NR-5 "p;" NR "p;" NR+5 "p\" " FILENAME

перевести на:

sed -n "NR-5p; NRp; NR+5p" FILENAME

NRэто номер строки, который соответствует шаблону matchи FILENAMEявляется текущим именем файла обработки, проходящим мимо awk.


2

используя пример текстового файла @ glenn и используя perl вместо awk:

$ perl -n0E 'say /(.*\n)(?=(?:.*\n){4}(.*match.*\n)(?:.*\n){4}(.*\n))/g' ex

даст те же результаты, но работает быстрее:

a
f match
k
d
i match
n

Жоао, ты появляешься в очереди на просмотр LQ и @waltinator проголосовал за удаление, так что в следующий раз будь чуть более многословным ... ;-) Также +1, чтобы вытащить тебя из очереди LQ ... : P
Fabby

1
@JJoao Очередь проверки низкого качества. Ваш ответ, вероятно, был найден там, потому что он был на 90% кодом.
wjandrea

1
@JJoao Цифра в 90% - это только мой способ объяснить это. Я не знаю, какие эвристики на самом деле используются.
wjandrea

1
Menos cafe, Mais Escrita! @JJoao : D ;-): D
Fabby

1
@Fabby: Sem café nada funciona: D - вероятно, это будет отображаться в LCQ (= низкая очередь кофе)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.