Как извлечь строку по шаблону с помощью grep, regex или perl - PullRequest
73 голосов
/ 22 февраля 2011

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

<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 подойдет.

Ответы [ 8 ]

136 голосов
/ 22 февраля 2011

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

Perl

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

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

GNU grep

Если у вас есть улучшенная версия grep, такая как GNU grep, у вас может быть доступна опция -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

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

5 голосов
/ 22 февраля 2011

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

5 голосов
/ 22 февраля 2011

Регулярное выражение будет:

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

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

4 голосов
/ 22 февраля 2011

Для этой цели следует использовать анализатор 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
2 голосов
/ 16 марта 2011

Вот решение, использующее 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
2 голосов
/ 22 февраля 2011

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

perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
1 голос
/ 16 марта 2011

Упс, команда 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
0 голосов
/ 02 декабря 2017

Если структура вашего 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 '"'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...