
У меня есть следующий текстовый файл.
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