Как извлечь строку по шаблону с помощью grep, regex или perl

55

У меня есть файл, который выглядит примерно так:

<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>

Мне нужно извлечь что-нибудь в кавычки, которые следуют за "name=", то есть content_analyzer, content_analyzer2 и content_analyzer_items.

Я делаю это в ящике Linux, поэтому решение с использованием sed, perl, grep или bash в порядке.

  • 4
    не нужно стесняться, добро пожаловать сюда!
  • 8
    Я чувствую, что было бы неправильно не ссылаться на stackoverflow.com/questions/1732348/…
Показать ещё 1 комментарий
Теги:
sed
html-parsing
text-extraction

8 ответов

105
Лучший ответ

Поскольку вам нужно сопоставить контент, не включая его в результат (необходимо соответствие name=", но это не часть желаемого результата). нумерация с нулевой шириной или групповой захват. Это можно сделать легко со следующими инструментами:

Perl

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

perl -ne 'print "$1\n" if /name="(.*?)"/' filename

GNU grep

Если у вас есть улучшенная версия grep, например GNU grep, у вас может быть доступна опция -P. Этот параметр позволит использовать Perl-подобное регулярное выражение, позволяя вам использовать \K, который является сокращенным lookbehind. Это будет reset позиция совпадения, поэтому все, что угодно, до нулевой ширины.

grep -Po 'name="\K.*?(?=")' filename

Параметр o позволяет grep печатать только согласованный текст, а не целая линия.

Vim - текстовый редактор

Другой способ - использовать текстовый редактор напрямую. С Vim один из различными способами этого было бы удаление строк без name=, а затем извлеките содержимое из результирующих строк:

:v/name=/d
:%s/\v.*name\="([^"]+)".*/\1

Стандартный grep

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

grep -o 'name="[^"]*"' filename

Заметка о сохранении результатов

Во всех командах выше результаты будут отправлены на stdout. Это важно помнить, что вы всегда можете сохранить их, подключив его к файл путем добавления:

> result

до конца команды.

  • 11
    Внешний вид (в GNU grep ): grep -Po '.*name="\K.*?(?=".*)'
  • 0
    @ Денис Уильямсон, отлично. Я соответственно обновил ответ, но оставил оба .* стороне, надеюсь, ты не сердишься на меня. Я хотел бы спросить, видите ли вы какие-либо преимущества от жадного совпадения над «чем угодно, кроме " ? Не воспринимайте это как бой, мне просто любопытно, и я не эксперт по регулярным выражениям. \K наконечник, очень приятно. Спасибо Деннис.
Показать ещё 10 комментариев
5

Если вы используете Perl, загрузите модуль для анализа XML: XML:: Simple, XML:: Twig, или XML:: LibXML. Не заново изобретайте колесо.

  • 3
    Обратите внимание, что пример, приведенный OP, не является правильно сформированным (например, <type="global" ), поэтому большинство анализаторов XML просто жалуются и умирают.
4

Регулярное выражение будет выглядеть следующим образом:

.+name="([^"]+)"

Тогда группировка будет в\1

4

Для этой цели следует использовать HTML-парсер, а не регулярные выражения. Программа Perl, использующая HTML::TreeBuilder:

Программа

#!/usr/bin/env perl

use strict;
use warnings;

use HTML::TreeBuilder;

my $tree = HTML::TreeBuilder->new_from_file( \*DATA );
my @elements = $tree->look_down(
    sub { defined $_[0]->attr('name') }
);

for (@elements) {
    print $_->attr('name'), "\n";
}

__DATA__
<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>

Выход

content_analyzer
content_analyzer2
content_analyzer_items
1

К сожалению, команда sed должна, прежде всего, следовать команде tidy:

echo "$htmlstr" | 
sed '/type="global"/d' |
tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null |
xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
1

Здесь используется решение с использованием HTML tidy и xmlstarlet:

htmlstr='
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
'

echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null |
sed '/type="global"/d' |
xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
  • 0
    Starlet хорошо работает для сценариев оболочки с поддержкой XML
1

это может сделать это:

perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
0

Если структура вашего xml (или текста вообще) исправлена, самый простой способ - использовать cut. Для вашего конкретного случая:

echo '<table name="content_analyzer" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
  <type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
  <type="global" />
</table>' | grep name= | cut -f2 -d '"'

Ещё вопросы

Сообщество Overcoder
Наверх
Меню