напечатать (только) строку перед совпадением

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

Рассмотрим ввод текста, который выглядит так:

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, вы можете эффективно работать с текстовыми данными, избегая лишних совпадений и разделителей.

Если у вас есть дополнительные требования или вопросы, пожалуйста, обращайтесь.

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

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