我有一个类似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
答案 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