Як витягти рядок за шаблоном за допомогою grep, регулярного виразу або perl


90

У мене є файл, який виглядає приблизно так:

    <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 - це нормально.


5
не потрібно соромитися, ласкаво просимо сюди!
Бенуа

8
Я вважаю, що було б неправильно не посилатись на stackoverflow.com/questions/1732348/…
Christoffer Hammarström

Дякую всім за корисні коментарі. Перепрошую за неправильне форматування XML. Я видалив деякі теги для спрощення.
суперечник

Відповіді:


167

Оскільки вам потрібно зіставити вміст, не включаючи його в результат (повинен збігатися, 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

до кінця команди.


12
Оглядові маршрути (у GNU grep):grep -Po '.*name="\K.*?(?=".*)'
Призупинено до подальшого повідомлення.

@ Денніс Вільямсон, чудово. Я відповідно оновив відповідь, але залишив обидва в .*стороні, сподіваюся, ти не злишся на мене. Я хотів би запитати, чи бачите ви якісь переваги від ненажерливого поєдинку над "чим завгодно ""? Не сприймайте це як бійку, мені просто цікаво і я не експерт з регулярних виразів. Крім того, \Kпідказка, дуже приємна. Дякую Деннісе.
sidyll

2
Чому б я сердився? Без цього .*можна обійтися grep -Po '(?<=name=").*?(?=")'. \KМоже бути використаний для стенографії, але це дійсно необхідно тільки , якщо матч з його лівим змінною довжиною. У таких випадках причина використання доріжок досить очевидна. Некоризні операції виглядають трохи акуратніше ( [^"]*проти, .*?і вам не потрібно повторювати якірний символ. Я не знаю швидкості. Я думаю, це багато в чому залежить від контексту. Сподіваюся, це корисно.
Призупинено до подальшого повідомлення.

@ Денніс Вільямсон: безумовно, сер, тут є багато корисної інформації. Я думаю, що причина, за якою я зберіг \K(після дослідження на ній) і видалив, .*була однакова: зробіть це гарним (простішим). І я ніколи не замислювався над тим, .*?щоб використати замість «традиційного способу», якому я десь навчився. Але ненажерливе тут справді має сенс. Дякую Деннісе, найкращі побажання.
sidyll

+1 для опису команди. Будемо вдячні, якщо ви зможете оновити свою відповідь, щоб пояснити "[...]" частину регулярного виразу.
lreeder


5

Якщо ви використовуєте Perl, завантажте модуль для синтаксичного аналізу XML: XML :: Simple , XML :: Twig або XML :: LibXML . Не вигадуйте заново колесо.


3
Зверніть увагу, що приклад наведеного OP не є добре сформованим ( <type="global"наприклад), тому більшість аналізаторів XML просто скаржаться і вмирають.
bvr

5

Для цього слід використовувати синтаксичний аналізатор 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

Ось рішення з використанням 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

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

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 '"'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.