0

我有一个包含大量配置数据的“类 XML”文件。我说“类似 XML”是因为它实际上就像 3 个 XML 文件连接在一起,用“]]>]]>”分隔

例如

<?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 对这些文件进行操作,那么它可以工作。

例如

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 。

感谢您提供的任何帮助。

-乔恩

4

2 回答 2

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

where/^\s*$/匹配在开头和结尾之间只有空格的记录,并!反转该匹配。

于 2015-01-21T16:07:49.183 回答
1

这是因为 print 命令的输出一直转到同一个 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

于 2015-01-21T16:20:31.750 回答