Печать конкретного раздела каждый раз при совпадении результатов поиска

Вопрос или проблема

У меня есть довольно простой текстовый файл на машине с Linux, в котором есть такие вещи, как главы, диалоги и ссылки.

Вот как он выглядит


Глава: 1 Один: Птицы и деревья

Птицы красивы, а деревья удивительны и
они зависят друг от друга. Птицы чаще всего
выбирают строить свои гнезда на деревьях, так как деревья обеспечивают больше
стабильности. Однажды птица села на дерево и сказала;

Птица: О, как же я устал от всех этих полетов, мне нужно отдохнуть
Дерево: Мистер Птица, вы кажетесь уставшим, возможно, вам стоит отдохнуть, и
вот несколько плодов, чтобы утолить жажду.
Птица: О, большое спасибо!

Ссылка:               Глава 1: птицы и деревья

Глава: 2 Два: Деревья и плоды

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

Ссылка:               Глава 2: деревья и плоды

Это содержание в txt-файле. Теперь, скажем, я искал утолить, я думал, что он будет начинаться с номера главы и до ссылки. Поэтому я попробовал с grep;

$ grep -A 5 -B 5 'утолить' file.txt

Однако это не дает желаемого результата. Я ожидал что-то вроде этого;

Глава: 1 Один: Птицы и деревья

Птицы красивы, а деревья удивительны и
они зависят друг от друга. Птицы чаще всего
выбирают строить свои гнезда на деревьях, так как деревья обеспечивают больше
стабильности. Однажды птица села на дерево и сказала;

Птица: О, как же я устал от всех этих полетов, мне нужно отдохнуть
Дерево: Мистер Птица, вы кажетесь уставшим, возможно, вам стоит отдохнуть, и
вот несколько плодов, чтобы утолить жажду.
Птица: О, большое спасибо!

Ссылка:               Глава 1: птицы и деревья

И также, поиск слова ‘витамин’ напечатал бы;

Глава: 2 Два: Деревья и плоды

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

Ссылка:               Глава 2: деревья и плоды

Мне было интересно, возможно ли это достичь с помощью sed или awk.

П.С: Каждая новая строка является фактической новой строкой

Одна идея с awk:

awk -v word="утолить" '                                            # установить переменную "word" в строку, которую мы ищем
/^Глава:/      { inblock = 1; block = "" }                      # начало нового блока; установить флаг и очистить блок

inblock          { block = block (block == "" ? "" : ORS) $0 }    # если inblock != 0, тогда добавьте текущую строку в "block"

/^Ссылка:/ &&                                                  # если конец блока и ...
block ~ word     { print block; inblock = 0 }                     # слово в блоке, тогда напечатать блок; очистить флаг
' file.txt

С -v word="утолить" это генерирует:

Глава: 1 Один: Птицы и деревья

Птицы красивы, а деревья удивительны и
они зависят друг от друга. Птицы чаще всего
выбирают строить свои гнезда на деревьях, так как деревья обеспечивают больше
стабильности. Однажды птица села на дерево и сказала;

Птица: О, как же я устал от всех этих полетов, мне нужно отдохнуть
Дерево: Мистер Птица, вы кажетесь уставшим, возможно, вам стоит отдохнуть, и
вот несколько плодов, чтобы утолить жажду.
Птица: О, большое спасибо!

Ссылка:               Глава 1: птицы и деревья

С -v word="необходимый" это генерирует:

Глава: 2 Два: Деревья и плоды

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

Ссылка:               Глава 2: деревья и плоды

С -v word="пузырь", или когда не предоставлены никакие -v word=... условия, это генерирует:

-- нет вывода

Используя Raku (ранее известный как Perl_6)

~$ raku -e 'my @a; for slurp() { @a = .comb( /^^Глава .*? <?before \nГлава | $ > /) };  @a.grep(/витамины/).put;'           file
slurp и проходит через comb, чтобы найти соответствующие записи (главы). Затем во втором выражении используется grep, чтобы вернуть только соответствующие записи (главы). Пример входных данных такой же, как предоставленный автором вопроса.

Пример вывода:

Глава: 1 Один: Птицы и деревья

Птицы красивы, а деревья удивительны и
они зависят друг от друга. Птицы чаще всего
выбирают строить свои гнезда на деревьях, так как деревья обеспечивают больше
стабильности. Однажды птица села на дерево и сказала;

Птица: О, как же я устал от всех этих полетов, мне нужно отдохнуть
Дерево: Мистер Птица, вы кажетесь уставшим, возможно, вам стоит отдохнуть, и
вот несколько плодов, чтобы утолить жажду.
Птица: О, большое спасибо!

Ссылка:               Глава 1: птицы и деревья

https://raku.org

Ответ или решение

Чтобы вывести определённый фрагмент текста, когда совпадают результаты поиска в текстовом файле на Linux, можно воспользоваться утилитами, такими как awk или sed. Данная задача требует организацию вывода так, чтобы он начинался с заголовка главы и заканчивался ссылкой при наличии совпадений с задействованными словами. Я предлагаю рассмотреть решение с использованием awk, так как оно позволяет более гибко обрабатывать текстовые блоки и управлять логикой вывода.

Пример содержимого файла

Файл содержит следующие блоки:

Chapter: 1 One: Birds and Trees

Birds are beautiful and trees are amazing and
...

Reference:               Chapter 1: birds and trees

Chapter: 2 Two: Trees and Fruits

Fruits are very delicious to eat and they are mostly found
...
Reference:               Chapter 2: trees and fruits

Использование awk

Для поиска и вывода содержимого глав, содержащих заданное слово, можно использовать следующую команду awk, где word – это переменная, которая будет содержать слово для поиска:

awk -v word="quench" '
    /^Chapter:/ { inblock = 1; block = "" }          # Начало нового блока, сбрасываем флаг и очищаем блок
    inblock { block = block (block == "" ? "" : ORS) $0 }  # Если в блоке, добавляем текущую строку
    /^Reference:/ && block ~ word { print block; inblock = 0 }  # Если конец блока и слово в блоке, выводим блок
' file.txt

Пояснение решения

  1. Инициализация: -v word="quench" задаёт переменную word с искомым значением. Вы можете менять это значение для поиска других слов.

  2. Обработка глав: При встрече строки, начинающейся с Chapter:, устанавливается флаг inblock, указывающий, что мы находимся внутри блока главы. В этот момент переменная block очищается.

  3. Сохранение содержимого: Как только мы заходим в блок, все строки добавляются в переменную block. Используется ORS, чтобы правильно добавить переносы строк между строками.

  4. Проверка на наличие слова: Когда мы достигли Reference:, выполняется проверка: если block содержит искомое слово, то содержимое выводится на экран, и флаг inblock сбрасывается.

Замена слова для поиска

Чтобы изменить слово для поиска, просто замените quench на любое другое слово, например, essential:

awk -v word="essential" '...'

Вывод результатов

При выполнении командных скриптов на примере искомого слова quench, вы получите следующий вывод:

Chapter: 1 One: Birds and Trees

Birds are beautiful and trees are amazing and
they are dependent on each other. Birds most of the time
choose to make their nests on trees since trees provide more
stability. One day the bird sat on a tree and said;

Bird: Oh my I'm so tired from all the flying, I should take a rest
Tree: Mr Bird, you seem tired, perhaps you should take some rest, and
here are some fruits to quench your thirst.
Bird: Oh thank you very much!

Reference:               Chapter 1: birds and trees

Заключение

Используя awk, можно значительно упростить задачу обработки текстовых данных. Удалив лишние операции, эта команда позволяет эффективно искать и выводить определённые блоки текста на основе заданных условий. Если вам нужна более сложная логика обработки текстов, можно использовать другие языки программирования, такие как Python или Raku, однако для большинства случаев awk будет более чем достаточен.

Оцените материал
Добавить комментарий

Капча загружается...