Здесь строки, кажется, создают новую строку в конце:
Вы можете попробовать и увидеть разницу:
cat <<<"test"
cat <(printf '%s' "test")
Таким образом, вы должны использовать < <(printf '%s' "$line")
вместо <<<"$(printf '%s' "$line")"
.
Обходной путь сawk
:
awk '/two/{ n=NR+5 } NR==n{ sub(/.*/, "MODIFIED") }1' file
или если вы хотите заменить строку
awk '/two/{ n=NR+5 } NR==n{ $0="MODIFIED" }1' file
/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
Построчный анализ:
N
. \(.*\n\)\{5\}
—, поместите это в группу захвата, оставьте последнюю строку(.*
)незахваченной и Modified
. 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:
:loop
и b
. \1
. Выполнение полного -совпадения строк и предположение, что такое совпадение не может -произойти в диапазоне 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
Для такого рода задач вам, возможно, придется подумать о том, что делать, если шаблон поиска повторяется до смещения.
awk -v offset=60 '
/two/ {x[NR + offset]}
NR in x {delete x[NR]; $0 = "MODIFIED"}
{print}'
Убедится, что строка заменяется всякий раз, когда строка на 60 строк выше содержит two
.
Компактное, но, возможно, не очень очевидное решение, которое должно работать с любой 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
снова Если вы редактируете файл, используйте соответствующий редактор вместо потокового редактора для редактирования в -месте . sed
предназначен для тех случаев, когда вам нужны возможности потоковой передачи.
В этом случае диапазоны и команды ed
s сильнее, чем POSIX sed
s:
printf '%s\n' 'g/two/+5s/.*/MODIFIED/' wq | ed -s file
И легко заметить/изменить 5
на, скажем, 60
.
(Вы можете сделать это источником для потока, если вы измените wq
на %p q
, два отдельных аргумента; однако у вас нет для печати всего файла. Вы также можете записать в другой файл с помощью 'w otherfile' q
, опять же, двух отдельных аргументов.)
Решение 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
awk -v l=60 '$0~/two/ && !s {s=1} s && ++s == l {$0 = "MODIFIED"; s=0}1'
Ищет совпадение (, только если оно еще не найдено ), и когда находит, устанавливает начальную позицию, затем увеличивает ее до предела. Когда предел достигнут, выполните замену и снова установите начальную позицию на 0.