Нежадное совпадение с регулярным выражением SED (имитация perl. *?)

ответ elbarna - это способ настройки параметров конфигурации "изначально", поддерживаемых libvirt (и, следовательно, его доменом XML). Для аргументов командной строки qemu (и других), не поддерживаемых libvirt, необходимо включить объявление пространства имен XML qemu в корневой элемент 'domain':


Ссылка: https://libvirt.org/drvqemu.html#qemucommand

37
23.07.2016, 08:47
6 ответов

В вашем случае вы можете просто инвертировать закрывающий символ следующим образом:

echo 'ssABteAstACABnnACss' | sed 's/AB[^C]*AC/XXX/'
0
27.01.2020, 19:42

Регулярные выражения Sed соответствуют самому длинному совпадению. У седла нет эквивалента нежадному.

Мы хотим сопоставить

  1. AB ,
    , за которым следует
  2. любое количество чего-либо, кроме AC ,
    {{ 1}}, за которым следует
  3. AC

К сожалению, sed не может выполнить №2 - , по крайней мере, не для многосимвольных регулярных выражений. Конечно, для односимвольного регулярного выражения, такого как @ (или даже [123]), мы можем сделать [^ @] * или [^ 123] * . Таким образом, мы можем обойти ограничения sed , заменив все вхождения AC на @ , а затем выполнив поиск

  1. AB ,
    , за которым следует
  2. любое число, кроме @ ,
    , за которым следует
  3. @

, например:

sed 's/AC/@/g; s/AB[^@]*@/XXX/; s/@/AC/g'

Последняя часть заменяет несопоставленные экземпляры @ обратно на AC .

Но это безрассудный подход , потому что ввод уже мог содержать @ символов. Таким образом, сопоставив их, мы могли получить ложные срабатывания. Однако , поскольку ни в одной переменной оболочки никогда не будет символа NUL ( \ x00 ), NUL, вероятно, будет хорошим символом для использования в описанном выше обходном пути вместо @ :

$ echo 'ssABteAstACABnnACss' | sed 's/AC/\x00/g; s/AB[^\x00]*\x00/XXX/; s/\x00/AC/g'
ssXXXABnnACss

Для использования NUL требуется GNU sed. (Чтобы убедиться, что функции GNU включены, пользователь не должен устанавливать переменную оболочки POSIXLY_CORRECT.)

Если вы используете sed с флагом GNU -z для обработки ввода, разделенного NUL, например вывод find ... -print0 , тогда NUL не будет в пространстве шаблонов, и NUL - хороший выбор для замены здесь.

Хотя NUL не может быть в переменной bash, его можно включить в команду printf . Если ваша входная строка может содержать любой символ, включая NUL, см. ответ Стефана Шазеласа , в котором добавлен умный метод экранирования.

29
20.08.2021, 12:02

Некоторые реализации sed поддерживают это. ssed имеет режим PCRE:

ssed -R 's/AB.*?AC/XXX/'

AT&T ast sed поддерживает оператор *? как нежадную версию * в своем ] расширенный -E ) и расширенный -A регулярными выражениями).

sed -E 's/AB.*?AC/XXX/'
sed -A 's/AB.*?AC/XXX/'

В этой реализации и в режимах -E / -A , в более общем смысле, регулярные выражения, подобные Perl, могут использоваться внутри (? P: Perl-like regexp здесь ) , хотя, как показано выше, это не обязательно для оператора *? .

Его расширенные регулярные выражения также имеют операторы соединения и отрицания:

sed -A 's/AB(.*&(.*AC.*)!)AC/XXX/'

В переносном смысле вы можете использовать этот метод: замените конечную строку (здесь AC ) одним символом, который не соответствует 'не встречается ни в начале, ни в конце строки (например, : здесь), поэтому вы можете сделать s / AB [^:] *: // , и в случае, если этот символ может появиться во вводе используйте механизм экранирования, который не конфликтует с начальной и конечной строками.

Пример:

sed 's/_/_u/g; # use _ as the escape character, escape it
     s/:/_c/g; # escape our replacement character
     s/AC/:/g; # replace the end string
     s/AB[^:]*:/XXX/; # actual replacement
     s/:/AC/g; # restore the remaining end strings
     s/_c/:/g; # revert escaping
     s/_u/_/g'

В GNU sed подход заключается в использовании новой строки в качестве символа замены. Поскольку sed обрабатывает одну строку за раз, новая строка никогда не появляется в пространстве шаблонов, поэтому можно сделать следующее:

sed 's/AC/\n/g;s/AB[^\n]*\n/XXX/;s/\n/AC/g'

Это обычно не работает с другими реализациями sed , потому что они не не поддерживает [^ \ n] . С GNU sed вы должны убедиться, что совместимость с POSIX не включена (как с переменной среды POSIXLY_CORRECT).

9
20.08.2021, 12:02

Нет, у sed regexes нет не жадного сопоставления.

Вы можете найти весь текст до первого вхождения AC, используя "anything not containing AC", за которым следует AC, что делает то же самое, что и .*?AC в Perl. Дело в том, что "все, что не содержит AC" не может быть легко выражено в виде регулярного выражения: всегда есть регулярное выражение, которое распознает отрицание регулярного выражения, но регекс отрицания быстро усложняется. А в переносимом sed это вообще невозможно, поскольку регекс отрицания требует группировки - чередования, которое присутствует в расширенных регулярных выражениях (например, в awk), но не в переносимых базовых регулярных выражениях. Некоторые версии sed, такие как GNU sed, действительно имеют расширения BRE, которые делают его способным выражать все возможные регулярные выражения.

sed 's/AB\([^A]*\|A[^C]\)*A*AC/XXX/'

Из-за сложности отрицания регулярного выражения это не очень хорошо обобщается. Вместо этого можно временно преобразовать строку. В некоторых реализациях sed в качестве маркера можно использовать новые строки, поскольку они не могут появляться в строке ввода (а если вам нужно несколько маркеров, используйте новую строку, за которой следует изменяющийся символ).

sed -e 's/AC/\
&/g' -e 's/AB[^\
]*\nAC/XXX/' -e 's/\n//g'

Однако имейте в виду, что backslash-newline не работает в наборе символов некоторых версий sed. В частности, это не работает в GNU sed, который является реализацией sed в Linux без расширения; в GNU sed вы можете использовать \n вместо этого:

sed -e 's/AC/\
&/g' -e 's/AB[^\n]*\nAC/XXX/' -e 's/\n//g'

В этом конкретном случае достаточно заменить первый AC на новую строку. Подход, который я представил выше, является более общим.

Более мощный подход в sed - сохранить строку в пространство hold, удалить все, кроме первой "интересной" части строки, поменять местами пространство hold и пространство шаблона или добавить пространство шаблона к пространству hold и повторить. Однако, если вы начинаете делать настолько сложные вещи, вам стоит задуматься о переходе на awk. В awk также нет жадного сопоставления, но вы можете разделить строку и сохранить части в переменных.

7
20.08.2021, 12:02

Решение очень простое. .*является жадным, но не совсем жадным. Рассмотрим сопоставление ssABteAstACABnnACssс регулярным выражением AB.*AC. AC, следующее за .*, должно иметь совпадение. Проблема в том, что поскольку .*жадный, последующий ACбудет соответствовать последнему AC, а не первому..*поглощает первый AC, в то время как литерал ACв регулярном выражении совпадает с последним в ssABteAstACABnn AC ss. Чтобы этого не произошло, просто замените первое ACчем-нибудь нелепым , чтобы отличить его от второго и от всего остального.

echo ssABteAstACABnnACss | sed 's/AC/-foobar-/; s/AB.*-foobar-/XXX/'
ssXXXABnnACss

Жадный .*теперь остановится у подножия -foobar-в ssABteAst-foobar-ABnnACss, потому что нет другого -foobar-, кроме этого -foobar-, и регулярное выражение-foobar-ДОЛЖНО иметь совпадение. Предыдущая проблема заключалась в том, что регулярное выражение ACимело два совпадения, но поскольку .*было жадным, было выбрано последнее совпадение для AC. Однако с -foobar-возможно только одно совпадение, и это совпадение доказывает, что .*не является абсолютно жадным. Автобусная остановка для .*происходит там, где только одно совпадение остается для остальной части регулярного выражения, следующего за .*.

Обратите внимание, что это решение не будет работать, если ACпоявится перед первым AB, потому что неправильный ACбудет заменен на -foobar-. Например, после первой замены sedACssABteAstACABnnACssстановится -foobar-ssABteAstACABnnACss; следовательно, совпадение с AB.*-foobar-не может быть найдено. Однако, если последовательность всегда...AB...AC...AB...AC..., то это решение будет успешным.

1
20.08.2021, 12:02

Один из вариантов — изменить строку таким образом, чтобы вы хотели жадное совпадение

echo "ssABtCeCAstACABnnACss" | rev | sed -E "s/(.*)CA.*BA(.*)/\1CA+-+-+-+-BA\2/" | rev

Используйте rev, чтобы инвертировать строку, инвертируйте критерии соответствия, используйте sedобычным способом, а затем инвертируйте результат....

ssAB-+-+-+-+ACABnnACss
1
20.08.2021, 12:02

Теги

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