Почему эта команда sed не заменяет третью с конца «и»?

Почему эта команда sed не заменяет третью с конца «и»?

Обновление 26.05.2020

Похоже, это была ошибка, поэтому я подал сообщение об ошибке. Ее идентификатор — #41558.


Я просто баловался sedи придумал такое упражнение: заменить третье с конца вхождение «and» (слово, а не подстроку), чтобы получилось:

dog XYZ foo and bar and baz land good

Я думал, это сработает.

echo 'dog and foo and bar and baz land good' |
    sed -E 's/(.*)\band\b((.*\band\b){2})/\1XYZ\2/'

но на самом деле он заменяет предпоследнее появление "and". Единственное объяснение, которое я могу придумать, это то, что он включает "land" как один из \band\b, но это не должно быть так, потому что я включил \bграницы слова?

решение1

Это трудно сделать, так как sedне поддерживает обходы и т. д. (как это можно сделать в PCRE). Было бы проще перевернуть строку и заменить третье вхождение перевернутого слова с самого начала, а затем перевернуть еще раз.

$ echo 'dog and foo and bar and baz land good' | rev | sed 's/\<dna\>/XXX/3' | rev
dog XXX foo and bar and baz land good

Что касается того, почему ваше выражение не работает, это похоже на ошибку. Обратная ссылка, \3похоже, является строкой  baz land, как будто \bbefore andin .*\band\bникогда не имел никакого эффекта.

Команда

sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'

похоже, что в OpenBSD все работает правильно с его собственным sed(который использует \<и \>вместо \b).

Мне еще предстоит найти существующий отчет об ошибке в GNU sedили GNU glibcпо этому поводу, хотя я не удивлюсь, если он будет, по крайней мересвязанныйкошибка glibc 25322(потому что, см. ниже).

Вы можете обойти это, если будете немного более многословны:

sed -E 's/(.*)\band\b(.*\band\b.*\band\b)/\1XYZ\2/'

решение2

Я бы предложил подать заявку на решение проблемы. Я протестировал эти примеры, и они дают одинаковое поведение с GNU grep, GNU sedи GNU awk. За исключением одного случая, который отмечен ниже.

  • Неправильный вывод:

    $ echo 'cocoa' | sed -nE '/(\bco){2}/p'
    cocoa
    

    sed -nE '/(\<co){2}/p'и awk '/(\<co){2}/'тоже имеет глючное поведение, но grep -E '(\<co){2}'правильно не выдает выходных данных

  • Правильное поведение, нет вывода:

    $ echo 'cocoa' | sed -nE '/\bco\bco/p'
    
  • itНеправильный вывод: после есть только одно целое словоwith

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
    it line XYZ too
    
  • Правильное поведение, входные данные не изменяются

    $ echo 'it line with it here sit too' | sed -E 's/with.*\bit\b.*\bit\b/XYZ/'
    it line with it here sit too
    
  • Изменение границ слов на \<и \>приводит к другой проблеме.

    Это правильноне изменяетвходные данные:

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here sit too
    

    Это правильно изменяет входные данные

    $ echo 'it line with it here it too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line XYZ too
    

    Но этот не может изменить входные данные.

    $ echo 'it line with it here it too sit' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here it too sit
    

Кроме того, проблемное поведение наблюдается только в том случае, если конфликтующее слово имеет дополнительные символы в начале. Например, itи sit. Но не в случае, если символы есть в конце. Например, itи siteи item.

$ echo 'it line with it here item too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
it line with it here item too
$ echo 'it line with it here it too item' | sed -E 's/with(.*\<it\>){2}/XYZ/'
it line XYZ too item

Связанный контент