2015-01-21 2 views
0

У меня есть XML-файл, содержащий множество данных конфигурации. Я говорю «XML-like», потому что это действительно как 3 XML-файла, объединенных вместе, разделенных «]]>]]>«Отступ XML-файл с awk и xmllint

E.g.

<?xml version="1.0" encoding="UTF-8"?> 
<hello><world>"Earth"</world></hello>]]>]]><?xml version="1.0" encoding="UTF-8"?> 
<data><lemur><type>"Ring-tailed"</type></lemur></data>]]>]]><?xml version="1.0" encoding="UTF-8"?> 
<data><lemur><type>"Mouse"</type></lemur></data>]]>]]> 

Я пытаюсь написать сценарий, который будет вызывать xmllint, чтобы отступать все теги XML в файле. Тем не менее, xmllint (и многие другие программы форматирования xml), похоже, требует, чтобы в файле был только один XML-документ. Например. файл должен начинаться с «<?xml version="1.0" encoding="UTF-8"?>» и содержать только одно корневое дерево.

Итак, я попробовал написать awk-скрипт, который будет анализировать данные на отдельные куски и передавать их на xmllint, но я получаю сообщение об ошибке, с которым я не могу пройти. Я поставил скрипт и вывод ниже.

$ awk ' 
BEGIN { 
    RS = "]]>]]>" 
    xmlFormatCommand = "xmllint --format -" 
} 

{ 
    print $0 | xmlFormatCommand 
} 
' SmallTest.xml 

-:3: parser error : XML declaration allowed only at the start of the document 
<?xml version="1.0" encoding="UTF-8"?> 
    ^
-:4: parser error : Extra content at the end of the document 
<data><lemur><type>"Ring-tailed"</type></lemur></data> 
^ 

Если бы я сделать это в два отдельных операциях, одна где AWK печатает на три временные файлы, и один, где xmllint работает над этими файлами, то он работает.

E.g.

awk 'BEGIN {RS = "]]>]]>"} {print $0 > "Section_" NR ".txt" }' SmallTest.xml 

В результате получается три файла Section_1.txt, Section_2.txt и Section_3.txt. Содержание Section_2.txt являются:

$ cat Section_2.txt 
<?xml version="1.0" encoding="UTF-8"?> 
<data><lemur><type>"Ring-tailed"</type></lemur></data> 

я могу форматировать файл с xmllint:

$ cat Section_2.txt | xmllint --format - 
<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <lemur> 
    <type>"Ring-tailed"</type> 
    </lemur> 
</data> 

Так что я не понимаю, почему я не могу просто труба это xmllint в первую очередь в awk-скрипте.

Я ценю любую помощь, которую вы можете предоставить.

-Jon

+0

я думаю, что форматирование объединённые файлы не будут работать из-за его структуры является недействительным. попробуйте удалить эти специальные charsequences']]> ', когда sed объединяет значения тегов данных, собранные из xmllint, используя встроенный xpath, создавая желаемую структуру, окружая теги выходных данных. надеюсь это поможет. –

+0

Спасибо за комментарий. Я не думаю, что]]>]]> отправляется в xmllint, так как это разделитель записей. –

ответ

1

Ваша проблема, в двух словах, что AWK продолжает использовать ту же трубу. Труба запоминается под той же самой строкой, с которой она была открыта (что означает, что вы не можете одновременно выполнять одну и ту же команду), и записи записываются в нее один за другим, поэтому у вас есть только один процесс xmllint который получает весь файл в качестве входных данных.

Вы можете это исправить, закрыв трубку после каждой записи:

$ awk ' 
BEGIN { 
    RS = "]]>]]>" 
    xmlFormatCommand = "xmllint --format -" 
} 

{ 
    print $0 | xmlFormatCommand 
    close(xmlFormatCommand)  # <-- HERE 
} 
' SmallTest.xml 

Здесь close принимает в качестве аргумента идентификатор, под которым труба запоминается (команда). Я знаю, что это выглядит странно по сравнению с другими языками программирования.

Поскольку у вас будет пустая запись в конце с файлом в вашем вопросе, кстати, вы можете захотеть поставить там условие, исключающее такие пустые записи. Например,

$ awk ' 
BEGIN { 
    RS = "]]>]]>" 
    xmlFormatCommand = "xmllint --format -" 
} 

! /^\s*$/ { # <-- HERE 
    print $0 | xmlFormatCommand 
    close(xmlFormatCommand) 
} 
' SmallTest.xml 

где /^\s*$/ матчи записи, которые имеют только пробелы между началом и концом, и ! инвертирует этот матч.

+0

Это было! Большое спасибо! –

1

Это связано с тем, что вывод команды печати продолжается в том же экземпляре xmllint.

Самый простой способ решить эту проблему, чтобы просто создать выходные файлы с xmllint тоже:

awk ' 
    BEGIN { 
    RS = "]]>]]>" 
} 
{ 
    print $0 | "xmllint --format --output sample_"NR".xml -" 
} 
' SmallTest.xml 

Если вы сделаете это, вы будете иметь один ошибка слева, потому что xmllint будет вызываться один раз после того, как последний строка без ввода ввода - так что вы можете просто удалить последний разделитель в исходном xml или проверить, имеет ли значение $ 0 в awk-скрипте.

Для вывода всего на стандартный вывод, сделайте следующее:

awk ' 
BEGIN { 
RS = "]]>]]>" 
} 
{ 
print $0 | "xmllint --format -" 
close("xmllint --format -")} 
' SmallTest.xml 

см https://www.gnu.org/software/gawk/manual/html_node/Close-Files-And-Pipes.html

+0

Могу ли я отправлять выходные данные на stdout, а не на отдельные файлы? –

+0

Жаль, этого не ответил другой плакат. – nlu