Я пытаюсь добавить текст в конец строки в первые x раз, когда он встречается. Я знаю, как это сделать глобально и для n-го вхождения. Я не могу понять, как это сделать для первых n-х вхождений. Примером может служить файл text.txt, который содержит:
This is a test
junk
This is a test
More junk
This is a test
This is a test
This is a test
И я хочу добавить '.' в конце первых трех раз, когда встречается "This is a test". Вывод, который я пытаюсь получить, следующий:
This is a test.
junk
This is a test.
More junk
This is a test.
This is a test
This is a test
решение1
This.*test
является правильным регулярным выражением. Звездочка означает "0 или более раз предыдущий символ", поэтому This*test
не будет соответствовать ни одной из ваших строк.
Теперь, Sed плох в арифметике. Для чего-то элегантного я предлагаю Awk:
awk '/This.*test/{c++};{print $0 (c<4 ? "." : "")}' file
Думаю, достаточно сказать, что c
, как и любая неустановленная переменная в Awk, она рассматривается как ноль, но дайте мне знать, если вам потребуются дополнительные разъяснения.
решение2
Другой вариант, который избегает выполнения сопоставления с регулярным выражением после того, как все 3 вхождения уже найдены:
awk -v n=3 'n && /This is a test/ {n--; $0 = $0 "."}; {print}'
В sed
частности, вы могли бы сделать что-то вроде:
sed '
1 {
x
s/^/.../
x
}
/This is a test/ {
s/$/./
x
s/.//
/./ {
x
b
}
g
:1
$! {
n
b 1
}
}'
Где мы отслеживаем количество .
добавляемых s как соответствующее количество .
s в удерживаемом пространстве.
Само собой разумеется, что sed
это гораздо менее подходит для такого рода задач. Если причина желания sed
заключается в -i
расширении для редактирования на месте, найденном в нескольких реализациях (заимствованном из perl
), обратите внимание, что реализация GNU awk
также может делать это с -i /usr/share/awk/inplace.awk
¹, или вы можете использовать настоящую вещь:
perl -lpi -e '
if ($n < 3 && /This is a test/) {
$n++;
$_ .= ".";
}' your-file
Если вы хотите добавить .
после каждого вхождения , This is a test
а не ко всем строкам, которые содержат хотя бы одно вхождение This is a test
, perl
также будет лучшим выбором:
perl -pi -e 's{This is a test\K}{$n++ < 3 ? "." : ""}ge' your-file
¹не использовать-i inplace
as сначала gawk
пытается загрузить inplace
расширение (как inplace
или inplace.awk
) из текущего рабочего каталога, где кто-то мог разместить вредоносное ПО. Путь расширения, inplace
поставляемого с, gawk
может различаться в зависимости от системы, см. выводgawk 'BEGIN{print ENVIRON["AWKPATH"]}'
решение3
С perl
мы могли бы сделать как показано
perl -lpe '
$_ = $k == 3 ? next : s/This is a test(?{$k++}).*\K/./r;
' file
Слоны тоже умеют танцевать, хотя и простые шаги. Используя GNU sed
запись в расширенном режиме регулярных выражений, -E
мы можем сохранить счетчик как количество новых строк в удержании.
K=3
sed -Ee '
/This is a test/!b
G
/(.*\n){'"$K"'}.*\n/!{
s/\n+/./p;z;H;d
}
s/\n+//
:a;n;ba
' file