регулярное выражение sed останавливается при первом совпадении

Я хочу заменить часть следующего html-текста (отрывок из огромного файла), чтобы обновить старое форматирование форума (результат очень плохого переноса форума, выполненного 2 года назад) на обычное форматирование phpBB:

    <blockquote id="quote"><font size="1" face="Verdana, Arial, Helvetica" id="quote">quote:<hr height="1" noshade id="quote"><i>written by User</i>

это должно быть отфильтровано в:

    [quote=User]

Я использовал следующее регулярное выражение в sed

    s/<blockquote.*written by \(.*\)<\/i>/[quote=\1]/g

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


person Ewout    schedule 09.06.2012    source источник
comment
sed с регулярным выражением Perl: ssed   -  person Lev Levitsky    schedule 10.06.2012


Ответы (3)


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

Самый простой способ сделать это — просто использовать Perl.

Если у вас есть существующий скрипт sed, вы можете перевести его на Perl с помощью утилиты s2p(1). Обратите внимание, что в Perl вы действительно хотите использовать $1 справа от оператора s///. В большинстве случаев \1 является устаревшим, но обычно вам нужен $1:

s/<blockquote.*?written by (.*?)<\/i>/[quote=$1]/g;

Обратите внимание, что я удалил обратную косую черту перед скобками. Еще одним преимуществом использования Perl является то, что он использует разумные регулярные выражения в стиле egrep (например, awk), а не уродливые регулярные выражения в стиле grep (например, sed), которые требуют всех этих запутанных (и непоследовательных) обратных косых черт повсюду.

Еще одно преимущество использования Perl заключается в том, что вы можете использовать парные вложенные разделители, чтобы избежать некрасивых обратных косых черт. Например:

s{<blockquote.*?written by (.*?)</i>}
 {[quote=$1]}g;

Еще одним преимуществом является то, что Perl отлично ладит с UTF-8 (сейчас это основная форма кодировки в Интернете), и что вы можете выполнять многострочные сопоставления без особых усилий, которые для этого требует sed. Например:

$ perl -CSD -00 -pe 's{<blockquote.*?written by (.*?)</i>}{[quote=$1]}gs' file1.utf8 file2.utf8 ...

-CSD заставляет его обрабатывать стандартный ввод, стандартный вывод и файлы как UTF-8. -00 заставляет читать весь файл одним глотком, а /s заставляет точку пересекать границы новой строки по мере необходимости.

person tchrist    schedule 09.06.2012
comment
Потрясающий! Забавно то, что я начал с Perl в первую очередь, но поскольку он якобы намного быстрее, я был соблазнен использовать sed... Не зная, что он так ограничен в этом. Не уверен, что -00 - хорошая идея, поскольку это файл размером 500 МБ (sql, содержащий html, я был неполным в первом сообщении). Большое спасибо!!! - person Ewout; 10.06.2012

Я не думаю, что sed поддерживает нежадное совпадение. Вы можете попробовать perl:

perl -pe 's/<blockquote.*?written by \(.*\)<\/i>/[quote=\1]/g' filename
person Hari Menon    schedule 09.06.2012
comment
Хорошая идея, но это не будет работать так, как у вас: вы забыли переключиться на шаблоны в стиле egrep с меньшим количеством обратных слэшей, поэтому вы ничего не записали. Смотрите мой ответ. - person tchrist; 10.06.2012

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

sed '/<blockquote.*written by .*<\/i>/!b;s/<blockquote/\n/g;s/\n[^\n]*written by \([^\n]*\)<\/i>/[quote=\1]/g;s/\n/\<blockquote/g' file

Объяснение:

  • Если строка не содержит шаблона, пропустите ее. /<blockquote.*written by .*<\/i>/!b
  • Измените переднюю часть шаблона на новую строку глобально по всей строке. s/<blockquote/\n/g
  • Глобально замените новую строку, за которой следует оставшийся шаблон, используя [^\n]* вместо .*. s/\n[^\n]*written by \([^\n]*\)<\/i>/[quote=\1]/g
  • Верните те новые строки, которые не были изменены, к исходному шаблону лицевой стороны. s/\n/\<blockquote/g
person potong    schedule 09.06.2012