awk: разобрать и записать в другой файл

У меня есть записи в XML-файле, как показано ниже. Мне нужно найти <keyword>SEARCH</keyword> и если он присутствует, мне нужно взять всю запись и записать в другой файл (начиная с <record> до </record> )

Ниже приведен код awk, который находится внутри цикла. $1 содержит значение строки по каждой записи.

 if(index($1,"SEARCH")>0) { print $1>> "output.txt" } 

Эта логика имеет две проблемы:

  1. Он записывает в файл output.txt только элемент <keyword>SEARCH</keyword> а не всю запись (начиная с <record> до </record> )
  2. ПОИСК может также присутствовать в <detail> . Этот код даже напишет этот тег на output.txt

Файл XML:

 <record category="xyz"> <person ssn="" ei="E"> <title xsi:nil="true"/> <position xsi:nil="true"/> <names> <first_name/> <last_name></last_name> <aliases> <alias>CDP</alias> </aliases> <keywords> <keyword xsi:nil="true"/> <keyword>SEARCH</keyword> </keywords> <external_sources> <uri>http://www.google.com</uri> <detail>SEARCH is present in abc for xyz reason</detail> </external_sources> </details> </record> <record category="abc"> <person ssn="" ei="F"> <title xsi:nil="true"/> <position xsi:nil="true"/> <names> <first_name/> <last_name></last_name> <aliases> <alias>CDP</alias> </aliases> <keywords> <keyword xsi:nil="true"/> <keyword>DONTSEARCH</keyword> </keywords> <external_sources> <uri>http://www.google.com</uri> <detail>SEARCH is not present in abc for xyz reason</detail> </external_sources> </details> </record> 

3 Solutions collect form web for “awk: разобрать и записать в другой файл”

Я собираюсь предположить, что то, что вы разместили, является образцом, потому что это неверный XML. Если это предположение неверно, мой ответ не выполняется … но если это так, вам действительно нужно поразить человека, который дал вам XML с завернутой копией спецификации XML, и потребовать, почини это'.

Но на самом деле – awk и регулярные выражения не являются подходящим инструментом для работы. Парсер XML. И с парсером, абсурдно просто сделать то, что вы хотите:

 #!/usr/bin/env perl use strict; use warnings; use XML::Twig; #parse your file - this will error if it's invalid. my $twig = XML::Twig -> new -> parsefile ( 'your_xml' ); #set output format. Optional. $twig -> set_pretty_print('indented_a'); #iterate all the 'record' nodes off the root. foreach my $record ( $twig -> get_xpath ( './record' ) ) { #if - beneath this record - we have a node anywhere (that's what // means) #with a tag of 'keyword' and content of 'SEARCH' #print the whole record. if ( $record -> get_xpath ( './/keyword[string()="SEARCH"]' ) ) { $record -> print; } } 

xpath довольно похож на регулярные выражения – в некотором роде – но это больше похоже на путь к каталогу. Это означает, что это контекст, и может обрабатывать структуры XML.

В приведенном выше: ./ означает «ниже текущего узла», поэтому:

 $twig -> get_xpath ( './record' ) 

Использует теги «верхнего уровня» <record> .

Но .// означает «на любом уровне ниже текущего узла», поэтому он будет делать это рекурсивно.

 $twig -> get_xpath ( './/search' ) 

Получил бы любые <search> узлы на любом уровне.

А квадратные скобки означают условие – это либо функция (например, text() чтобы получить текст узла), либо вы можете использовать атрибут. например //category[@name] найдет любую категорию с атрибутом name, а //category[@name="xyz"] будет отфильтровывать их дальше.

XML, используемый для тестирования:

 <XML> <record category="xyz"> <person ssn="" ei="E"> <title xsi:nil="true"/> <position xsi:nil="true"/> <details> <names> <first_name/> <last_name></last_name> </names> <aliases> <alias>CDP</alias> </aliases> <keywords> <keyword xsi:nil="true"/> <keyword>SEARCH</keyword> </keywords> <external_sources> <uri>http://www.google.com</uri> <detail>SEARCH is present in abc for xyz reason</detail> </external_sources> </details> </person> </record> <record category="abc"> <person ssn="" ei="F"> <title xsi:nil="true"/> <position xsi:nil="true"/> <details> <names> <first_name/> <last_name></last_name> </names> <aliases> <alias>CDP</alias> </aliases> <keywords> <keyword xsi:nil="true"/> <keyword>DONTSEARCH</keyword> </keywords> <external_sources> <uri>http://www.google.com</uri> <detail>SEARCH is not present in abc for xyz reason</detail> </external_sources> </details> </person> </record> </XML> 

Вывод:

  <record category="xyz"> <person ei="E" ssn=""> <title xsi:nil="true" /> <position xsi:nil="true" /> <details> <names> <first_name/> <last_name></last_name> </names> <aliases> <alias>CDP</alias> </aliases> <keywords> <keyword xsi:nil="true" /> <keyword>SEARCH</keyword> </keywords> <external_sources> <uri>http://www.google.com</uri> <detail>SEARCH is present in abc for xyz reason</detail> </external_sources> </details> </person> </record> 

Примечание. Вышеприведенная версия просто печатает запись в STDOUT. Это на самом деле … по-моему, не такая прекрасная идея. Не в последнюю очередь потому, что – он не печатает структуру XML, и поэтому он не является фактически «допустимым» XML, если у вас более одной записи (нет «корневого» узла).

Поэтому вместо этого я хотел бы выполнить именно то, что вы спрашиваете:

 #!/usr/bin/env perl use strict; use warnings; use XML::Twig; my $twig = XML::Twig -> new -> parsefile ('your_file.xml'); $twig -> set_pretty_print('indented_a'); foreach my $record ( $twig -> get_xpath ( './record' ) ) { if ( not $record -> findnodes ( './/keyword[string()="SEARCH"]' ) ) { $record -> delete; } } open ( my $output, '>', "output.txt" ) or die $!; print {$output} $twig -> sprint; close ( $output ); 

Вместо этого – инвертирует логику и удаляет (из анализируемой структуры данных в памяти) записи, которые вам не нужны, и печатает всю новую структуру (включая заголовки XML) в новый файл с именем «output.txt».

Если я правильно понял, это может быть решением в awk !:

 /^<record/ { x1=""; while (match($0, "record>$")==0) { x1=x1 $0"\n"; getline; } x1=x1 $0; if (x1 ~ />SEARCH</) { print x1 > "output.txt"; } } 

Это извлечет блоки, запись> в \ запись>, содержащую ключ «ПОИСК» внутри, в выходной файл.

Кроме того, awk (то же, что и другие текстовые процессоры) не является подходящим инструментом xml-parsing:

 awk ' lines{ lines=lines "\n" $0 } /<\/record/{ if(lines ~ /keyword>SEARCH</) print lines lines="" } /<record/{ lines=$0 } ' <input.txt >output.txt 

То же, что sed

 sed -n '/<record/{:1;N;/<\/record/!b1;/keyword>SEARCH</p;}' <input.txt >output.txt 
  • Переупорядочить текст в каждой строке файла
  • Как я могу разобрать xml-файл с URL-адреса http без загрузки файла и распечатать нужную строку?
  • Vim: Создание XML-текста «довольно»
  • Как настроить файл ~ / .tidyrc для XML?
  • XML модифицирует и записывает в файл красивый суп
  • Поиск строки, с успехом Поиск ближайшего шаблона
  • XML-parsing с чистой баш
  • Как проверить корректность XML-файла 4 ГБ?
  • Как узнать содержимое файла XML с помощью Unix Sed / Awk?
  • поиск и замена строки
  • как сравнить два xml-файла с одинаковыми данными в разных строках?
  • Linux и Unix - лучшая ОС в мире.