Вопрос или проблема
У меня есть довольно простой текстовый файл на машине с 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: птицы и деревья
Ответ или решение
Чтобы вывести определённый фрагмент текста, когда совпадают результаты поиска в текстовом файле на 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
Пояснение решения
-
Инициализация:
-v word="quench"
задаёт переменнуюword
с искомым значением. Вы можете менять это значение для поиска других слов. -
Обработка глав: При встрече строки, начинающейся с
Chapter:
, устанавливается флагinblock
, указывающий, что мы находимся внутри блока главы. В этот момент переменнаяblock
очищается. -
Сохранение содержимого: Как только мы заходим в блок, все строки добавляются в переменную
block
. ИспользуетсяORS
, чтобы правильно добавить переносы строк между строками. -
Проверка на наличие слова: Когда мы достигли
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
будет более чем достаточен.