使用awk和xmllint缩进类似XML的文件

时间:2015-01-21 15:38:07

标签: xml bash awk format xmllint

我有一个类似XML的"包含大量配置数据的文件。我说" XML-like"因为它真的像3个XML文件连接在一起,用"]]>]]>"

分隔

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文档。例如。该文件需要以&#34; <?xml version="1.0" encoding="UTF-8"?>&#34;开头。并且只包含一个根树。

所以我尝试编写一个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>

所以我不明白为什么我不能在awk脚本中首先将它传输到xmllint。

感谢您提供的任何帮助。

-Jon

2 个答案:

答案 0 :(得分:1)

简而言之,你的问题是awk继续使用相同的管道。管道在与它打开的完全相同的字符串下被记住(这意味着你不能同时运行两次完全相同的命令),并且记录一个接一个地写入它,所以你只有一个{{ 1}}将整个文件作为输入的进程。

您可以通过在每条记录后关闭管道来解决此问题:

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*$/个反转。

答案 1 :(得分:1)

这是因为print命令的输出保持与xmllint的同一个实例。

解决此问题的最简单方法是使用xmllint创建输出文件:

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

如果你这样做,你将剩下一个错误,因为xmllint将在最后一行之后被调用一次而没有任何输入 - 所以你可以删除源xml中的最后一个分隔符或检查$ 0是否有值在awk脚本中。

要将所有内容输出到stdout,请执行:

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