Вопрос или проблема
Рассмотрим ввод текста, который выглядит так:
a
b
pattern
c
pattern
d
e
pattern
a
--
pattern
z
Я хотел бы получить только строки, которыеprecede a match.
Например, если мой шаблон — pattern
, вывод должен быть таким:
b
c
e
--
Очевидное решение с grep -B1
не работает, так как оно
- включает совпадающую строку
- добавляет разделители, если есть несколько несовпадающих строк
Например:
$ grep -B1 pattern
b
pattern
c
pattern
--
e
pattern
--
--
pattern
Наверняка я мог бы отфильтровать pattern
, но
- я думаю, что это некрасиво
- это не решает проблему с разделителем
--
$ grep -B1 pattern test.txt | grep -v pattern
b
c
--
e
--
--
Я очевидно не могу отфильтровать разделитель --
, так как это может быть строка, которая меня интересует:
$ grep -B1 pattern test.txt | grep -v pattern | grep -v "^--$"
b
c
e
Я полагаю, что какой-то хитрый прием awk
или sed
может мне помочь, но я не знаю, какой…
Решение должно работать в POSIX оболочке, с установленными только стандартными инструментами (даже без Python!)
Perl обычно установлен на всех Linux, поэтому я предлагаю вам эту однострочную команду на Perl:
Это выводит предыдущую строку $p
, если текущая строка содержит pattern
, затем сохраняет текущую строку в $p
. Для каждой строки входного файла.
perl -ane 'print $p if /pattern/; $p=$_' inputfile
Вывод:
b
c
e
--
Ответ или решение
Как напечатать только строку перед совпадением с заданным шаблоном
Вопрос о том, как вывести строки, предшествующие совпадениям с определённым шаблоном в текстовом файле, является распространенной задачей в работе с текстовыми данными. Давайте рассмотрим, как можно эффективно решить эту задачу с использованием стандартных инструментов командной строки, таких как awk
и sed
.
Проблема
Исходные данные представлены в следующем формате:
a
b
pattern
c
pattern
d
e
pattern
a
--
pattern
z
Ваша цель — вывести только строки, которые предшествуют каждому вхождению строки pattern
. Ожидаемый результат:
b
c
e
--
Метод с использованием awk
Один из самых простых и элегантных способов решения этой задачи заключается в использовании инструмента awk
, который позволяет обрабатывать текстовые данные построчно:
awk '{if (prev && /pattern/) print prev; prev=$0}' inputfile.txt
Разбор команды:
prev
— это переменная, которая хранит предыдущую строку.if (prev && /pattern/) print prev;
— эта строка проверяет, есть ли значение вprev
(предыдущая строка), и совпадает ли текущая строка с шаблоном. Если обе проверки истинны,awk
выводит значение переменнойprev
.prev=$0
— эта часть команды обновляетprev
на текущую строку.
Этот подход позволяет избежать вывода строки с шаблоном и сохраняет все строки, включая разделители (такие как --
), что соответствует вашим критериям.
Альтернативный метод с использованием sed
Если вам удобнее использовать sed
, можно добиться аналогичного результата с небольшой хитростью:
sed -n 'x; /^pattern/{x; p;}; x' inputfile.txt
Объяснение команды:
-n
— отключает автоматический вывод вsed
, он будет печатать только то, что мы явно укажем.x
— обмен значениями между регистром и буфером.(/^pattern/{x; p;}; x)
— когда текущая строка совпадает сpattern
, выполняется обмен строк и выводится предшествующая строка.
Заключение
Эти методы позволяют получить нужный результат, выводя только строки перед заданным шаблоном в текстовом файле. Используя awk
или sed
, вы можете эффективно работать с текстовыми данными, избегая лишних совпадений и разделителей.
Если у вас есть дополнительные требования или вопросы, пожалуйста, обращайтесь.