Заменить n-ную строку из соответствующего шаблона

Заменить n-ную строку из соответствующего шаблона

У меня есть следующий текстовый файл.

banana
apple
juice
mango
something

Я ищу шаблон juiceи хочу найти вторую строку из этого совпадающего шаблона в обратном порядке (т. е. на 2 строки выше совпадающего шаблона) и заменить ее на coconut.

Ожидаемый результат:

coconut
apple
juice
mango
something

Я попробовал сделать следующее, но он просто удаляет две вышеуказанные строки, а не ту, которую я ищу.

tac foo.txt |sed '/juice/I,+2 d' |tac
mango
something

Думаю, что корректировка приведенного выше скрипта решит эту проблему, но я не уверен.

Примечание: совпадение не будет повторяться, и оно не обязательно должно быть точным (то есть совпадение может быть найдено и в длинной строке). Совпадение должно быть чувствительным к регистру.

решение1

Если edвсе в порядке, вам нужно редактировать файл, а не поток, и он только один  juice:

$ more <<-EOF | ed -s ./tmp.txt
	/juice/
	-2
	c
	coconut
	.
	w
	q
EOF
$

Найдите строку, перейдите на две строки вверх, cизмените, wобведите и  qуберите.


Еще более компактный вариант, предложенный @d-ben-knoble в комментариях:

$ printf '%s\n' '/^juice$/-2s/.*/coconut/' w q | ed -s ./tmp.txt

решение2

Следуя вашему подходу,

tac file|sed '/juice/{n;n;s/.*/coconut/}'|tac
  • /juice/соответствует строке с juice.
  • n;n;печатает текущую и следующую строку.
  • s/.*/coconut/производит замену.

Судя по всему, у вас есть GNU sed, поэтому вы также можете использовать его -zдля загрузки всего файла в память и непосредственного редактирования строки два выше juice,

sed -rz 's/[^\n]*(\n[^\n]*\n[^\n]*juice)/coconut\1/' file

[^\n]означает «не новая строка», а скобки ()фиксируют группу, воспроизводимую обратной \1ссылкой.

решение3

$ tac file | awk 'c&&!(--c){$0="coconut"} /juice/{c=2} 1' | tac
coconut
apple
juice
mango
something

решение4

Вот еще один awkподход, на этот раз метод двойного прохода:

awk 'NR==FNR&&/juice/{m=FNR} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file
  • Указываем файлдва разав качестве аргументов командной строки, чтобы он обрабатывался дважды.
  • На первом проходе (где FNR, счетчик строк для каждого файла, равен NR, глобальному счетчику строк), мы просто определяем, в какой строке juiceвстречается шаблон поиска, и сохраняем его в переменной m.
  • Во втором проходе мы устанавливаем номер строки m-2для заменяющего текста coconut.
  • Как правило, мы печатаем строки, включая любые изменения, но только во втором проходе (где условие NR>FNRоценивается как «истинное»).

Если у вас GNU awk(некоторые другие awkреализации также поддерживают это), вы можете немного ускорить процесс, прервав первый проход сразу после того, как будет найдено совпадение, с помощью команды nextfile:

awk 'NR==FNR&&/juice/{m=FNR;nextfile} NR>FNR&&FNR==m-2{$0="coconut"} (NR>FNR)' file file

Связанный контент