Работает ли lookbehind в sed?

Я создал тест, используя grep, но он не работает в sed.

grep -P '(?<=foo)bar' file.txt

Это работает правильно, возвращая bar.

sed 's/(?<=foo)bar/test/g' file.txt

Я ожидал footest в качестве вывода, но это не сработало.


person Matheus Gontijo    schedule 29.09.2014    source источник
comment
sed не поддерживает утверждения просмотра.   -  person hwnd    schedule 30.09.2014


Ответы (2)


GNU sed не поддерживает утверждения просмотра. Вы можете использовать более мощный язык, такой как Perl, или поэкспериментировать с ssed, который поддерживает регулярные выражения в стиле Perl.

perl -pe 's/(?<=foo)bar/test/g' file.txt
person hwnd    schedule 29.09.2014
comment
Текст, сопровождающий ваше решение, не совсем имеет смысла, поскольку Perl также не поддерживает PCRE (по крайней мере, изначально). - person ikegami; 30.09.2014

Обратите внимание, что в большинстве случаев вы можете избежать просмотра назад (или просмотра вперед), используя группу захвата и обратную ссылку в строке замены:

sed 's/\(foo\)bar/\1test/g' file.txt

Имитация отрицательного взгляда назад более тонкая и требует нескольких замен для защиты подстроки, которую вы хотите избежать. Пример для (?<!foo)bar:

sed 's/#/##/g;s/foobar/foob#ar/g;s/bar/test/g;s/foob#ar/foobar/g;s/##/#/g' file.txt
  • выберите escape-символ и повторите его (например, # => ##).
  • включите этот символ в подстроку, которую вы хотите защитить (здесь foobar, => foob#ar или ba => b#a).
  • сделай свою замену.
  • замените foob#ar на foobar (или b#a на ba).
  • замените ## на #.

Очевидно, вы также можете описать все, что не foo до bar:

sed -E 's/(^.{0,2}|[^f]..|[^o].?)bar/\1test/g' file.txt

Но это будет быстро привередливым с большим количеством символов.

person Casimir et Hippolyte    schedule 29.09.2014
comment
Но это не работает для отрицательного взгляда назад, например. вы хотите, чтобы bar НЕ перед foo был заменен на test, что будет сделано (если это сработает) с /(?‹!foo)bar/test/. Кто-нибудь решение этого? (Я хочу использовать uniq в 5-м поле файла SQL, но предыдущие поля могут содержать пробелы, поэтому у меня нет лучшей идеи, чем заменить все пробелы НЕ между...', '... на _...) - person Max; 18.11.2018
comment
@Max: 1) выберите escape-символ и повторите его (например, # => ##). 2) включите этот символ в подстроку, которую вы хотите защитить (здесь foobar => foob#ar). 3) произвести замену. 4) заменить foob#ar на foobar. 5) заменить ## на #. Пример с sed: sed 's/#/##/g;s/foobar/foob#ar/g;s/bar/test/g;s/foob#ar/foobar/g;s/##/#/g' <<<'abc foobar # foob#ar foo bar' - person Casimir et Hippolyte; 11.06.2020
comment
Хорошо, да, это работает, по сути, вы удаляете то, что хотите защитить (может быть, foobar=›-#- (вместо foob#ar) было бы понятнее), затем вы находите и заменяете все остальные, затем ставите защищенные назад - person Max; 11.06.2020
comment
@Max: foobar => -#-: если хотите (и только если вы ранее заменили все # на ##). - person Casimir et Hippolyte; 11.06.2020