Заменить N-ю строку после совпадения

Здесь строки, кажется, создают новую строку в конце:

Вы можете попробовать и увидеть разницу:

cat <<<"test"
cat <(printf '%s' "test")

Таким образом, вы должны использовать < <(printf '%s' "$line")вместо <<<"$(printf '%s' "$line")".

8
14.05.2021, 08:04
8 ответов

Обходной путь сawk:

awk '/two/{ n=NR+5 } NR==n{ sub(/.*/, "MODIFIED") }1' file

или если вы хотите заменить строку

awk '/two/{ n=NR+5 } NR==n{ $0="MODIFIED" }1' file
11
28.07.2021, 11:32
/two/{
    :loop
    N
    /\(\(.*\n\)\{5\}\).*/{
        s//\1Modified/
        b
    }
    b loop
}

Сохраните это в x.sedи выполните как

sed -f x.sed file

или используйте один вкладыш -для GNU Sed:

sed '/two/{:a;N;/\(\(.*\n\)\{5\}\).*/{s//\1Modified/;b;};ba;}' file

Построчный анализ:

  • 1 -2 :Когда совпадение найдено, запустите цикл. В этой петле
    • 3 :Добавьте строку в пространство шаблона с помощью команды N.
    • 4 :Если есть 5 символов новой строки — основное регулярное выражение \(.*\n\)\{5\}—, поместите это в группу захвата, оставьте последнюю строку(.*)незахваченной и
      • 5 :Замените все пространство шаблонов*захватом группа, за которой следует строка Modified.
      • 6 :Разорвать петлю.
    • 8 :Еще loopеще раз.

*Пустой слот регулярного выражения эквивалентен ранее использовавшемуся регулярному выражению, так что 5-я строка эквивалентна s/\(\(.*\n\)\{5\}\).*/\1Modified/.


Менее эзотерическим является использование Awk:

awk '{c--};/two/{c=5};c==0{$0="Modified"};{print}' file

Это начинается с уменьшения cсчетчика, и поскольку, как и все переменные, cизначально нулевой, он становится положительным только в том случае, если совпадение найдено.

Еще более явным подходом был бы

awk 'BEGIN{c=-1};/two/{c=5};c==0{$0="Modified"};{print};c>=0{c=c-1}' file

Полезные ресурсы Sed:

6
28.07.2021, 11:32

Выполнение полного -совпадения строк и предположение, что такое совпадение не может -произойти в диапазоне 5 строк, используя любой awk в любой оболочке на каждом компьютере Unix:

$ awk 'c&&!--c{$0="MODIFIED"} $0=="two"{c=5} 1' file
one
two
three
four
five
six
MODIFIED
eight
nine
ten
.
.
.
five-hundred

См. https://stackoverflow.com/questions/17908555/printing-with-sed-or-awk-a-line-following-a-matching-pattern/17914105#17914105.

4
28.07.2021, 11:32

Для такого рода задач вам, возможно, придется подумать о том, что делать, если шаблон поиска повторяется до смещения.

awk -v offset=60 '
    /two/ {x[NR + offset]}
  NR in x {delete x[NR]; $0 = "MODIFIED"}
          {print}'

Убедится, что строка заменяется всякий раз, когда строка на 60 строк выше содержит two.

9
28.07.2021, 11:32

Компактное, но, возможно, не очень очевидное решение, которое должно работать с любой sedразновидностью и не требует зацикливания:

sed -e 'H;/^two$/h;x;/^two\(\n.*\)\{5\}/{s//MODIFIED/;x;}' -e x

Идея :Собрать строки в резервном пространстве, начиная с соответствующей строки. Если в удерживаемом пространстве есть n новых строк, мы просто добавили заменяемую строку, поэтому заменяем все удерживаемое пространство и пробелы переключения.

Подробно:

  • Hдобавит любую строку в пробел
  • /^two$/hбудет копировать строку с совпадающим шаблоном в пространство для хранения, перекрывая все строки, которые мы собрали ранее, и убедившись, что пространство для хранения начинается с заданного шаблона, который мы передали
  • xменяет местами оба буфера, чтобы мы могли разобраться с тем, что мы собрали в трюме. Обратите внимание, что мы изменим буферы обратно в конце скрипта
  • .
  • /^two\(\n.*\)\{5\}/— это паттерн, начинающийся со строки триггера, и как минимум еще 5 строк (заменяют 5желаемым значением ). Таким образом, этот шаблон совпадет, как только мы добавим пять строк после совпадающей. Эврика! Что делать сейчас? Мы хотим напечатать замену И нам нужно уничтожить пространство удержания, чтобы оно не подходило снова, пока не будет найдена другая триггерная строка. Мы можем сделать обе вещи одновременно, заменив весь материал заменой:
  • s//MODIFIED/заменяет все наше пространство удержания (, которое находится в пространстве шаблонов, начиная с команды x), с заменой. Отсутствие первого шаблона команды sприводит к использованию последнего шаблона.
  • Теперь еще один xменяет буферы на случай, если мы сделали замену, поэтому изменение обратных буферов на последнем шаге снова приведет нашу замену в пространство шаблонов для печати. Затем новое пространство удержания будет содержать удаленную строку,не запускать совпадение в будущем, если оно не было twoснова
0
28.07.2021, 11:32

Если вы редактируете файл, используйте соответствующий редактор вместо потокового редактора для редактирования в -месте . sedпредназначен для тех случаев, когда вам нужны возможности потоковой передачи.

В этом случае диапазоны и команды eds сильнее, чем POSIX seds:

printf '%s\n' 'g/two/+5s/.*/MODIFIED/' wq | ed -s file

И легко заметить/изменить 5на, скажем, 60.

(Вы можете сделать это источником для потока, если вы измените wqна %p q, два отдельных аргумента; однако у вас нет для печати всего файла. Вы также можете записать в другой файл с помощью 'w otherfile' q, опять же, двух отдельных аргументов.)

4
28.07.2021, 11:32

Решение sed(, но только GNU sed )без циклов и с использованием простых диапазонов:

sed '/^two$/,+5{/^two$/,+4{b};s/.*/& hello/}'

Единственное, что нужно настроить, — это соответствие регулярным выражениям и счетчикам +5 и +4.

За 100 строк после матча:

sed '/two/,+100{/two/,+99{b};s/.*/& Modified/}'

Возможно, это прояснит, как это работает:

$ seq 10 | sed '/4/,+3{/4/,+2{s/.*/&changed/;b};s/.*/& Modified.../}'
1
2
3
4changed
5changed
6changed
7 Modified...
8
9
10

5
28.07.2021, 11:32
awk -v l=60 '$0~/two/ && !s {s=1} s && ++s == l {$0 = "MODIFIED"; s=0}1'

Ищет совпадение (, только если оно еще не найдено ), и когда находит, устанавливает начальную позицию, затем увеличивает ее до предела. Когда предел достигнут, выполните замену и снова установите начальную позицию на 0.

0
28.07.2021, 11:32

Теги

Похожие вопросы