Как извлечь строку по шаблону с помощью grep, regex или perl
У меня есть файл, который выглядит примерно так:
<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 вполне подойдет.
Ответы
Ответ 1
Так как вам нужно сопоставлять контент, не включая его в результат (должен соответствовать name="
но он не должен быть частью желаемого результата), требуется некоторая форма сопоставления нулевой ширины или захвата группы. Это можно легко сделать с помощью следующих инструментов:
Perl
С Perl вы можете использовать опцию n
чтобы построчно циклически выводить содержимое группы захвата, если она соответствует:
perl -ne 'print "$1\n" if /name="(.*?)"/' filename
GNU grep
Если у вас есть улучшенная версия grep, такая как GNU grep, у вас может быть -P
опция -P
. Эта опция активирует Perl-подобное регулярное выражение, позволяя вам использовать \K
что является сокращенным представлением. Это сбросит позицию совпадения, поэтому все, что находится до нулевой ширины.
grep -Po 'name="\K.*?(?=")' filename
Опция o
заставляет grep печатать только соответствующий текст, а не всю строку.
Vim - текстовый редактор
Другой способ - использовать текстовый редактор напрямую. В Vim один из различных способов сделать это - удалить строки без name=
а затем извлечь содержимое из результирующих строк:
:v/.*name="\v([^"]+).*/d|%s//\1
Стандартный grep
Если по какой-то причине у вас нет доступа к этим инструментам, чего-то подобного можно добиться с помощью стандартного grep. Однако без осмотра это потребует некоторой очистки позже:
grep -o 'name="[^"]*"' filename
Примечание о сохранении результатов
Во всех вышеперечисленных командах результаты будут отправлены на стандартный stdout
. Важно помнить, что вы всегда можете сохранить их, отправив их в файл, добавив:
> result
до конца команды.
Ответ 2
Регулярное выражение будет выглядеть следующим образом:
.+name="([^"]+)"
Тогда группировка будет в\1
Ответ 3
Если вы используете Perl, загрузите модуль для анализа XML: XML:: Simple, XML:: Twig, или XML:: LibXML. Не заново изобретайте колесо.
Ответ 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
Ответ 5
это может сделать это:
perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
Ответ 6
Здесь используется решение с использованием 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
Ответ 7
К сожалению, команда 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
Ответ 8
Если структура вашего 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 '"'