строки в столбцы с awk

Я должен следовать примеру вывода:

<HARDWARE> <NAME>WIN1</NAME> <OS>Windows 7</OS> <IP>1.2.3.4</IP> <DOMAIN>contoso.com</DOMAIN> </HARDWARE> <HARDWARE> <NAME>WIN2</NAME> <OS>Windows 8</OS> <IP>10.20.30.40</IP> <DOMAIN>contoso.com</DOMAIN> </HARDWARE> 

Каков наилучший способ разобрать его, чтобы он выглядел так:

 WIN1 Windows 7 1.2.3.4 contoso.com WIN2 Windows 8 10.20.30.40 contoso.com 

Ищете решение для использования стандартных инструментов, таких как awk, sed и т. Д.

  • Замена всего `&`, но не единственного в XML-сущности `& amp;` с `#` с sed в заданном теге XML
  • Заменить текст с помощью sed и сохранить часть исходного текста
  • Bash / Linux Инструменты для исправления xml
  • Parse JSON или XML при загрузке
  • Эффективное извлечение данных из нескольких файлов в один файл CSV
  • Вставить многострочную строку в другую строку
  • Извлекать и удалять первое вхождение XML-тега несколько раз
  • Как получить список значений соответствия атрибутов с помощью xmllint и xpath?
  • 5 Solutions collect form web for “строки в столбцы с awk”

    Пожалуйста, не используйте awk sed и т. Д. Они не могут правильно обрабатывать XML . XML делает кучу вещей, таких как пробелы, переводы строк, унарные теги и т. Д., Что означает, что регулярные выражения не очень надежны – они прерываются беспорядочно, следуя совершенно правильному изменению XML по строке.

    Способ обработки XML – это синтаксический анализатор. xmlstarlet обычно используется в Linux. Потому что я еще не видел, чтобы это предлагалось – я бы использовал perl. Например:

     #!/usr/bin/perl use strict; use warnings; use XML::Twig; my $twig = XML::Twig -> parsefile ('your_xml_file.xml'); foreach my $HW ( $twig -> findnodes ( '//HARDWARE' ) ) { print join ( "\t", map { $_ -> text } $HW -> children ),"\n"; } 
    • Разбор XML
    • итерации элементов HARDWARE .
    • Извлечь text из детей
    • принт это.

    Вы можете немного расширить его, чтобы вы могли обрабатывать, например, различные наборы / порядок полей:

     #!/usr/bin/perl use strict; use warnings; use XML::Twig; my @fields_to_show = qw ( OS NAME ); my $twig = XML::Twig -> parsefile ( 'your_filename.xml' ); foreach my $HW ( $twig -> findnodes ( '//HARDWARE' ) ) { my %fields = map { $_ -> tag => $_ -> text } $HW -> children; print join ("\t", @fields{@fields_to_show}),"\n"; } 

    Он генерирует хэш (ассоциативный массив), называемый %fields которые выглядят как (для каждого элемента):

     $VAR1 = { 'OS' => 'Windows 7', 'NAME' => 'WIN1', 'DOMAIN' => 'contoso.com', 'IP' => '1.2.3.4' }; 

    И затем мы используем @fields_to_show чтобы указать, что отображать и в каком порядке.

    Таким образом, это будет печатать:

     Windows 7 WIN1 Windows 8 WIN2 

    NB: Я также должен «исправить» ваш XML, потому что без одного корневого тега это неверно. Другие ответы упомянули об этом. Спецификация XML довольно строгая – сломанный XML должен быть отклонен. Таким образом, на самом деле довольно плохая форма для «исправления» XML, и обычно я предлагаю ударить того, кто сгенерировал ее вокруг головы, с копией спецификации XML.

    data.xml свой XML- data.xml , оберните весь свой XML в родительский <DATA> 1 или другой, который вы data.xml , файл с именем data.xml :

     <DATA> <HARDWARE> <NAME>WIN1</NAME> <OS>Windows 7</OS> <IP>1.2.3.4</IP> <DOMAIN>contoso.com</DOMAIN> </HARDWARE> <HARDWARE> <NAME>WIN2</NAME> <OS>Windows 8</OS> <IP>10.20.30.40</IP> <DOMAIN>contoso.com</DOMAIN> </HARDWARE> </DATA> 

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

      xmlstarlet sel -T -t -m /DATA/HARDWARE -v "concat(NAME,' ',OS,' ',IP,' ',DOMAIN)" -n data.xml | column -t 

    дает:

     WIN1 Windows 7 1.2.3.4 contoso.com WIN2 Windows 8 10.20.30.40 contoso.com 

    Редактировать:

    Основываясь на замечательном замечании Peter.O в комментариях и его ответе ниже , давайте отправим вывод с разделителями column -ts$'|' на column -ts$'|' , поэтому что-то вроде:

     xmlstarlet sel --indent-tab -T -t -m /DATA/HARDWARE -v "concat(NAME,'|',OS,'|',IP,'|',DOMAIN)" -n data.xml | column -ts$'|' 

    Теперь поля прекрасно выравниваются, даже если у них есть пробелы:

     WIN1 Windows 7 1.2.3.4 release 5 contoso.com Really long OS X Windows 8 10.20.30.40 contoso.com 

    1. Или используйте { echo '<DATA>'; cat file_name; echo '</DATA>'; } | xmlstarlet ... { echo '<DATA>'; cat file_name; echo '</DATA>'; } | xmlstarlet ... { echo '<DATA>'; cat file_name; echo '</DATA>'; } | xmlstarlet ... как отмечает Peter.O в комментарии ниже

    2. Использование пространства в качестве разделителя не выравнивает столбцы должным образом

    С вашим примером и GNU sed:

     sed -n 's/<[^>]*>//g;s/^ *//g;/./p' file | paste -d ";" - - - - | column -t -s ";" 

    Вывод:

     WIN1 Windows 7 1.2.3.4 contoso.com
     WIN2 Windows 8 10.20.30.40 contoso.com
    

    Я предполагаю, что ваш файл не содержит ; , Если вам нужен CSV remove | column -t -s ";" | column -t -s ";" ,

    Следующий скрипт awk (плюс column для вывода табуляции) будет передавать любую последовательность размещения суб- тэгов и любое разделение пробелов между тегами – т.е. он будет обрабатывать формат ввода выборки OP, а также следующий образец, который не имеет пробелов и по- разному упорядоченных подтег:

      <HARDWARE><OS>Windows 7</OS><IP>1.2.3.4</IP><DOMAIN>contoso.com</DOMAIN><NAME>WIN1</NAME></HARDWARE><HARDWARE><NAME>WIN2</NAME><OS>Windows 8</OS><DOMAIN>contoso.com</DOMAIN><IP>10.20.30.40</IP></HARDWARE> 

     awk 'BEGIN{ RS="[[:space:]]*</?HARDWARE>[[:space:]]*" FS="[[:space:]]*<|</[^<>/]+>[[:space:]]*" tn=split( "NAME OS IP DOMAIN", tag_order, " " ) } $0 { delete tag for( i=1;i<=NF;i++ ) if($i) { n=index($i,">"); tag[substr($i,1,n-1)]=substr($i,n+1) } for( i=1;i<=tn;i++ ) printf "%s\t", tag[tag_order[i]]; print "" }' file | column -ts$'\t' 

    вывод:

     WIN1 Windows 7 1.2.3.4 contoso.com WIN2 Windows 8 10.20.30.40 contoso.com 

    с awk – произвольно задавать каждый столбец длиной 15 символов, выравнивать по левому краю и заполнять пробелами:

     awk ' BEGIN { FS = "<[A-Za-z/]+>" } { if ( NR % 6 == 0 ) { printf"\n" } else if ( $2 != "" ) { printf"%-15s", $2 } }' file 

    Или как в других ответах в сочетании со column

     awk ' BEGIN { FS = "<[A-Za-z/]+>" } { if ( NR % 6 == 0 ) { printf"\n" } else if ( $2 != "" ) { printf"%s ", $2 } }' file | column -t 
    Interesting Posts

    Проблема с DNS> Если я отключу рекурсию, я не могу выполнить ping мой результат

    понять выход bsdlabel

    Как измерить тактовый импульс моего компьютера вручную?

    wicd – нет связи после загрузки, необходимо перезапустить вручную?

    Какое программное обеспечение можно использовать для создания живого экрана в Linux?

    Есть ли стороннее приложение, которое может общаться через сеть Skype?

    В чем смысл ошибок из моей команды cpio?

    Как клонировать / копировать все атрибуты файла / каталога в другой файл / каталог?

    Поддерживает ipod с помощью dd, как получить все треки за один раз с gnupod?

    Перенаправление портов SNMP в маршрутизатор для использования с привязкой SNMP openHAB

    как установить имя хоста в yast?

    Я установил java, и он работает, но когда я пытаюсь вернуть Java домой, он пуст

    Действительно ли необходимо обновить все пакеты до обновления Fedora с помощью DNF?

    как я могу скрыть «ctags: Предупреждение: игнорирование тега null в"

    Как сделать отказоустойчивость HA Heartbeat, когда потеряно одно из двух соединений NIC?

    Linux и Unix - лучшая ОС в мире.