Выполните команду в одной строке несколько раз с помощью sed

мне нужно выделить каждое повторяющееся слово в тексте с * символ.
Например

lol foo lol bar foo bar

должно быть

lol foo *lol* bar *foo* *bar*

я попробовал со следующей командой:

echo "lol foo lol bar foo bar" | sed -r -e 's/(b[a-zA-Z]+b)([^*]+)()/**/'

это дает мне:

lol foo *lol* bar foo bar

затем я добавил g флаг:

lol foo *lol* bar foo *bar*

но foo не выделены.
Я знаю, что это происходит потому, что sed не оглядывается, если матч был найдено.

могу ли я справиться только с sed?

2 ответов


Sed - это не лучший инструмент для этой задачи. Он не смотрит вперед, не смотрит назад и не жадные кванторы, но попробуйте следующую команду:

sed -r -e ':a ; s/\b([a-zA-Z]+)\b(.*) ()( |$)/ ** / ; ta'

он использует условное ветвление для выполнения команды подстановки, пока не произойдет сбой. Кроме того, вы не можете проверить ([^*]+) потому что для второго раунда он должен пересечь некоторые * из первой замены, ваш вариант является жадным .*. И, наконец, вы не можете соответствовать () только потому, что он будет соответствовать первому строка lol снова и снова. Вам нужен какой-то контекст, например, окруженный пробелами или концом строки.

команду дает:

lol foo *lol* bar *foo* *bar*

обновление: улучшение предоставлен potong комментарии:

sed -r ':a;s/\b(([[:alpha:]]+)\s.*\s)\b/**/;ta' file

используя awk

awk '{for (i=1;i<=NF;i++) if (a[$i]++>=1) printf "*%s* ",$i; else printf "%s ",$i; print ""}' file
lol foo *lol* bar *foo* *bar*