Извлечение данных из простого файла XML - PullRequest
39 голосов
/ 08 февраля 2010

У меня есть XML-файл с содержанием:

<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>

Мне нужен способ для извлечения того, что находится в тегах <job..> </job>, в данном случае - programmin. Это должно быть сделано в командной строке Linux, используя grep / sed / awk.

Ответы [ 9 ]

62 голосов
/ 08 февраля 2010

Вы действительно должны использовать только эти инструменты? Они не предназначены для обработки XML, и хотя в большинстве случаев можно получить что-то, что работает нормально, оно не будет работать в крайних случаях, таких как кодирование, разрывы строк и т. Д.

Я рекомендую xml_grep:

xml_grep 'job' jobs.xml --text_only

Что дает вывод:

programming

В ubuntu / debian xml_grep входит в пакет xml-twig-tools.

12 голосов
/ 08 февраля 2010
 grep '<job' file_name | cut -f2 -d">"|cut -f1 -d"<"
9 голосов
/ 10 июня 2015

Пожалуйста, не используйте синтаксический анализ строк и регулярных выражений в XML. Это плохая идея. У вас может быть семантически идентичный XML с различным форматированием, и регулярное выражение и анализ на основе строк просто не справляются с этим.

Такие вещи, как унарные тэги и переменные переносы строк - эти фрагменты "говорят" одно и то же:

<root>
  <sometag val1="fish" val2="carrot" val3="narf"></sometag>
</root>


<root>
  <sometag
      val1="fish"
      val2="carrot"
      val3="narf"></sometag>
</root>

<root
><sometag
val1="fish"
val2="carrot"
val3="narf"
></sometag></root>

<root><sometag val1="fish" val2="carrot" val3="narf"/></root>

Надеюсь, это прояснит, почему создание синтаксического анализатора на основе регулярных выражений / сложностей? К счастью, вам не нужно. Многие языки сценариев имеют по крайней мере один, иногда больше параметров синтаксического анализатора.

Как упоминалось в предыдущем постере - доступно xml_grep. На самом деле это инструмент, основанный на Perl-библиотеке XML::Twig. Однако он использует «выражения xpath», чтобы найти что-то, и проводит различие между структурой документа, атрибутами и «контентом».

например:.

xml_grep 'job' jobs.xml --text_only

Однако, в интересах лучшего ответа, вот пара примеров «накатить свои» на основе ваших исходных данных:

Первый способ:

Используйте twig handlers, который ловит элементы определенного типа и воздействует на них. Преимущество такого способа заключается в том, что он анализирует XML «по ходу дела» и позволяет вам изменять его в полете, если это необходимо. Это особенно полезно для отбрасывания «обработанного» XML при работе с большими файлами, используя purge или flush:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new(
    twig_handlers => {
        'job' => sub { print $_ ->text }
    }
    )->parse( <> );

Который будет использовать <> для получения ввода (переданного по каналу или указанного через командную строку ./myscript somefile.xml) и его обработки - каждый элемент job извлечет и напечатает любой связанный текст. (Возможно, вы захотите print $_ -> text,"\n" вставить перевод строки).

Поскольку он соответствует элементам 'job', он также будет соответствовать вложенным элементам job:

<job>programming
    <job>anotherjob</job>
</job>

Будет совпадать дважды, но вывести часть выходных данных также дважды. Тем не менее, вы можете выбрать /job вместо этого, если хотите. Полезно - это позволяет вам, например, распечатайте и удалите элемент или скопируйте и вставьте элемент, модифицирующий структуру XML.

В качестве альтернативы - сначала выполнить синтаксический анализ, и 'print' на основе структуры:

my $twig = XML::Twig->new( )->parse( <> );
print $twig -> root -> text;

Поскольку job является вашим корневым элементом, все, что нам нужно сделать, это распечатать его текст.

Но мы можем быть немного более проницательными, ищем job или /job и печатаем это специально вместо:

my $twig = XML::Twig->new( )->parse( <> );
print $twig -> findnodes('/job',0)->text;

Вы также можете использовать опцию XML::Twig s pretty_print для переформатирования вашего XML:

XML::Twig->new( 'pretty_print' => 'indented_a' )->parse( <> ) -> print;

Существует множество вариантов формата вывода, но для более простого XML (такого как ваш) большинство будет выглядеть примерно одинаково.

9 голосов
/ 02 июля 2010

Использование xmlstarlet:

echo '<job xmlns="http://www.sample.com/">programming</job>' | \
   xmlstarlet sel -N var="http://www.sample.com/" -t -m "//var:job" -v '.'
8 голосов
/ 09 февраля 2010

просто используйте awk, другие внешние инструменты не нужны. Внизу работает, если ваши нужные теги появляются в мультитине.

$ cat file
test
<job xmlns="http://www.sample.com/">programming</job>
<job xmlns="http://www.sample.com/">
programming</job>

$ awk -vRS="</job>" '{gsub(/.*<job.*>/,"");print}' file
programming

programming
5 голосов
/ 10 февраля 2010

Принимая ту же строку, ввод из stdin:

sed -ne '/<\/job>/ { s/<[^>]*>\(.*\)<\/job>/\1/; p }'

примечания: -n останавливает вывод всего автоматически; -e означает, что это однострочный (или сценарий) /<\/job> действует как grep; s удаляет атрибуты opentag + и endtag; ; - новое утверждение; p отпечатков; {} заставляет grep применяться к обоим операторам как к одному.

3 голосов
/ 08 февраля 2016

Использование команды sed :

Пример:

$ cat file.xml
<note>
        <to>Tove</to>
                <from>Jani</from>
                <heading>Reminder</heading>
        <body>Don't forget me this weekend!</body>
</note>

$ cat file.xml | sed -ne '/<heading>/s#\s*<[^>]*>\s*##gp'
Reminder

Пояснение: 1012 *
*

cat file.xml | sed -ne '/<pattern_to_find>/s#\s*<[^>]*>\s*##gp'

n - запретить печать всех строк
e - скрипт

/<pattern_to_find>/ - находит строки, содержащие указанный шаблон, например, <heading>

следующая часть подстановки s///p, которая удаляет все, кроме требуемого значения, где / заменяется на # для лучшей читаемости:

s#\s*<[^>]*>\s*##gp
\s* - включает пробелы, если они существуют (то же самое в конце)
<[^>]*> представляет <xml_tag> как не жадное регулярное выражение, альтернативная причина <.*?> не работает для sed
g - заменяет все, например закрывающий тег xml </xml_tag>

0 голосов
/ 06 декабря 2015

Немного опоздал на шоу.

xmlcutty вырезает узлы из XML:

$ cat file.xml
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
<job xmlns="http://www.sample.com/">designing</job>
<job xmlns="http://www.sample.com/">managing</job>
<job xmlns="http://www.sample.com/">teaching</job>

Аргумент path указывает путь к элементу, который вы хотите вырезать. В этом случае, поскольку теги вообще не интересуют нас, мы переименовываем тег в \n, поэтому получаем хороший список:

$ xmlcutty -path /job -rename '\n' file.xml
programming
designing
managing
teaching

Обратите внимание, что XML был недопустим для начала (без корневого элемента). xmlcutty также может работать со слегка поврежденным XML.

0 голосов
/ 08 февраля 2010

Как насчет:

cat a.xml | grep '<job' | cut -d '>' -f 2 | cut -d '<' -f 1
...