Как использовать sed для поиска и замены шаблона, который появляется несколько раз в одной строке? - PullRequest
2 голосов
/ 19 сентября 2019

Поскольку вопрос может вводить в заблуждение, приведу небольшой пример.У меня есть такой файл:

some text
some text @@some-text-KEY-some-other-text@@
text again @@some-text-KEY-some-other-text@@ @@some-text-KEY-some-other-text@@
again @@some-text-KEY-some-other-text-KEY-text@@
some text with KEY @@KEY-some-text@@
blabla @@KEY@@

В этом примере я хочу заменить каждое вхождение KEY- в паре @@ на VALUE-.Я начал с этой команды sed:

sed -i 's/\(@@[^@]*\)KEY-\([^@]*@@\)/\1VALUE-\2/g'

Вот как это работает:

  1. \(@@[^@]*\): создать первую группу, состоящую из двух @ и любых символов, кроме @ ...
  2. KEY-: ... до последнего вхождения KEY- в этой строке
  3. \([^@]*@@\): и создать вторую группу со всеми символами, кроме @ до следующей пары @.

Проблема в том, что моя команда не может правильно обработать следующую строку, поскольку в моей паре @@ есть несколько *1029*:

again @@some-text-KEY-some-other-text-KEY-text@@

Действительно, я получаю такой результат:

again @@some-text-KEY-some-other-text-VALUE-text@@

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

Как я могу создать регулярное выражение и команду sed, которые могут правильно обрабатывать все мои файлы?

Ответы [ 2 ]

3 голосов
/ 19 сентября 2019

Проблема довольно сложная: вам нужно заменить все вхождения текста из нескольких символов в блоках текста между одинаковыми разделителями из нескольких символов.

Самый простой и безопасный способ решения задачи -используя Perl:

perl -i -pe 's/(@@)(.*?)(@@)/$end_delim=$3; "$1" . $2=~s|KEY-|VALUE-|gr . "$end_delim"/ge' file

См. онлайн-демонстрацию .

Шаблон (@@)(.*?)(@@) будет сопоставлять строки между двумя смежными подстроками @@, захватывающими начальный разделитель в группу1, конечный разделитель в группе 3 и весь текст между ними в группе 2. Поскольку подстановка регулярного выражения переустанавливает все заполнители, временная переменная используется для сохранения значения конечного разделителя ($end_delim=$3), затем "$1" . $2=~s|KEY-|VALUE-|gr . "$end_delim" заменяет совпадение значением в группе 1 первого совпадения (первым @@), затем значение группы 2 со всеми KEY-, заменяемыми на VALUE-, и затем конечным разделителем.

Если между матчами в одной строке нет KEY- с , вы можете использовать ветку с sed, заключив свою команду в :A и tA:

sed -i ':A; s/\(@@[^@]*\)KEY-\([^@]*@@\)/\1VALUE-\2/g; tA' file

Обратите внимание, что вы пропустили первый заполнитель в \VALUE-\2, это должно быть \1VALUE-\2.

См. онлайн-демонстрацию :

s="some KEY- text
some text @@some-text-KEY-some-other-text@@
text again @@some-text-KEY-some-other-text@@ @@some-text-KEY-some-other-text@@
again @@some-text-KEY-some-other-text-KEY-text@@
some text with KEY @@KEY-some-text@@
blabla @@KEY@@"

sed ':A; s/\(@@[^@]*\)KEY-\([^@]*@@\)/\1VALUE-\2/g; tA' <<< "$s"

Вывод:

some KEY- text
some text @@some-text-VALUE-some-other-text@@
text again @@some-text-VALUE-some-other-text@@ @@some-text-VALUE-some-other-text@@
again @@some-text-VALUE-some-other-text-VALUE-text@@
some text with KEY @@VALUE-some-text@@
blabla @@KEY@@

Подробнее :

sed позволяет использовать петли и ветви .:A в приведенном выше коде - это метка , особый маркер местоположения, на который можно «прыгнуть», используя соответствующий оператор.t используется для создания ветви, эта команда переходит на метку, только если предыдущая команда замены была успешной".Таким образом, как только шаблон сопоставлен и замена произошла, sed возвращается туда, где он был, и повторяет попытку сопоставления.Если это не удалось, sed продолжает поиск совпадений в строке.Таким образом, tA означает вернуться к месту, отмеченному A, если была успешная операция поиска и замены .

0 голосов
/ 23 сентября 2019

Это может работать для вас (GNU sed):

sed -E 's/@@/\n/g;:a;s/^([^\n]*(\n[^\n]*\n[^\n]*)*\n[^\n]*)KEY-/\1VALUE-/;ta;s/\n/@@/g' file

Преобразование @@ в новые строки.Используя цикл, замените VAL- между соответствующими символами новой строки на VALUE-.Когда все сделано, замените символы новой строки на @@.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...