用于提取和验证 xml 个文件的 awk 管道

awk pipeline to extract and validate xml files

如何在管道中使用 awk 和 xmllint 提取和验证 xml 文件。

只提取文件的awk程序:

提取物xml

#!/usr/bin/awk -f
/<?xml version/{ getline doctype; getline datadoc;
     if (match(datadoc,/file="([^-]+)-[^"]+.XML"/,a)) {
         fn=a[1]".xml"; print [=11=] ORS doctype ORS datadoc > fn; print a[1]".xml" ; next;
     }}{ print > fn }

输入串联 xml 文件:

refcase.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data-document SYSTEM "refcase.dtd" [ ]>
<data-document lang="EN" dtd-version="v1 2017-01-01" file="aa1234aa-20170101.XML">
<document-metatdata lang="EN" country="INTL">
<document-reference/>
</document-metatdata>
</data-document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data-document SYSTEM "refcase.dtd" [ ]>
<data-document lang="EN" dtd-version="v1 2017-01-01" file="aa2345bb-20170202.XML">
<document-metatdata lang="EN" country="LOCAL">
<document-reference/>
</document-metatdata>
</data-document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data-document SYSTEM "refcase.dtd" [ ]>
<data-document lang="EN" dtd-version="v1 2017-01-01" file="aa3456cc-20170303.XML">
<document-metatdata lang="EN" country="NA">
<document-reference/>
</document-metatdata>
</data-document>

验证命令:

xmllint --debug --dtdvalid refcase.dtd aa1234bb.xml

XML xmllint 用于验证 xml 文件的 dtd 文件:

refcase.dtd

<?xml encoding="UTF-8"?>

<!ELEMENT data-document (document-metatdata)>
<!ATTLIST data-document
  xmlns CDATA #FIXED ''
  date-published CDATA #REQUIRED
  dtd-version CDATA #REQUIRED
  file NMTOKEN #REQUIRED

<!ELEMENT document-metatdata (document-reference)>
<!ATTLIST document-metatdata
  xmlns CDATA #FIXED ''
  country NMTOKEN #REQUIRED
  lang NMTOKEN #REQUIRED>

<!ELEMENT document-reference EMPTY>
<!ATTLIST document-reference
xmlns CDATA #FIXED ''>

当我将此代码添加到 awk 程序时:

{ print > fn } system("xmllint --debug --dtdvalid refcase.dtd " fn " > " a[1]".xml.rpt")
  1. awk 提取仍然可以正常工作,.xml 文件和以前一样创建。
  2. awk 输出现在传递给 xmllint 命令以进行 xml 验证,看起来 xmllint 命令的输入有问题。

提取文件并将输出发送到 xmllint 命令的 Awk 程序:

#!/usr/bin/awk -f
/<?xml version/{ getline doctype; getline datadoc;
     if (match(datadoc,/file="([^-]+)-[^"]+.XML"/,a)) {
         fn=a[1]".xml"; print [=16=] ORS doctype ORS datadoc > fn; print a[1]".xml" ; next;
     }}{ print > fn } system("xmllint --debug --dtdvalid refcase.dtd " fn " > " a[1]".xml.rpt")

在 awk 中调用时 xmllint 命令的问题输出:

aa1234aa.xml
aa1234aa.xml:5: parser error : Premature end of data in tag document-metatdata line 4
aa1234aa.xml:5: parser error : Premature end of data in tag data-document line 3
<document-metatdata lang="EN" country="INTL">
aa1234aa.xml:6: parser error : Premature end of data in tag document-metatdata line 4
aa1234aa.xml:6: parser error : Premature end of data in tag data-document line 3
<document-reference/>
aa1234aa.xml:7: parser error : Premature end of data in tag data-document line 3

在shell中执行命令时不会出现解析器错误,只有在awk程序中执行时才会出现错误。这对我来说表明提取的 xml 文件没问题。

这是对数千个串联的 txt 文件的提取过程,每个文件包含数千个 xml 文件。我需要跟踪和审核所有步骤并验证输出。

提取的 xml 个文件的预期输出:

aa1234aa.XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data-document SYSTEM "refcase.dtd" [ ]>
<data-document lang="EN" dtd-version="v1 2017-01-01" file="aa1234aa-20170101.XML">
<document-metatdata lang="EN" country="INTL">
<document-reference/>
</document-metatdata>
</data-document>

aa2345bb.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data-document SYSTEM "refcase.dtd" [ ]>
<data-document lang="EN" dtd-version="v1 2017-01-01" file="aa2345bb-20170202.XML">
<document-metatdata lang="EN" country="LOCAL">
<document-reference/>
</document-metatdata>
</data-document>

aa3456cc.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE data-document SYSTEM "refcase.dtd" [ ]>
<data-document lang="EN" dtd-version="v1 2017-01-01" file="aa3456cc-20170303.XML">
<document-metatdata lang="EN" country="NA">
<document-reference/>
</document-metatdata>
</data-document>

问题:

我希望 awk 将输出写入文件并将输出重定向到命令以进行进一步处理。

不确定 awk 是否是最好的提取工具,到目前为止它在测试数据上运行良好。我需要记录过程并验证输出。

感谢任何其他可靠且可扩展的方法吗?

您发布的命令是:

/<?xml version/{ getline doctype; getline datadoc;
     if (match(datadoc,/file="([^-]+)-[^"]+.XML"/,a)) {
         fn=a[1]".xml"; print [=10=] ORS doctype ORS datadoc > fn; print a[1]".xml" ; next;
     }}{ print > fn } system("xmllint --debug --dtdvalid refcase.dtd " fn " > " a[1]".xml.rpt")

第 1 步是修复它以使用合理的格式,以便我们可以看到控制流:

/<?xml version/{
     getline doctype
     getline datadoc;
     if (match(datadoc,/file="([^-]+)-[^"]+.XML"/,a)) {
         fn=a[1]".xml"
         print [=11=] ORS doctype ORS datadoc > fn
         print a[1]".xml"
         next
     }
}
{ print > fn }
system("xmllint --debug --dtdvalid refcase.dtd " fn " > " a[1]".xml.rpt")

好的,现在我们一眼就可以看到 system() 调用是在一个条件块中而不是一个动作中,它没有关闭输出文件,它没有引用 xmllint文件名,它在多个地方硬编码 a[1]".xml" 所以让我们修复这些:

/<?xml version/{
     getline doctype
     getline datadoc
     if (match(datadoc,/file="([^-]+)-[^"]+.XML"/,a)) {
         close(fn)
         fn=a[1]".xml"
         print [=12=] ORS doctype ORS datadoc > fn
         print fn
         next
     }
}
{
    print > fn
    system("xmllint --debug --dtdvalid refcase.dtd 7" fn "7 > 7" fn ".rpt7")
}

现在让我们摆脱对 getline 的脆弱和不必要的调用:

/<?xml version/{
    xmlversion = [=13=]
    cnt = 3
}
cnt==2 {
    doctype = [=13=]
}
cnt==1 {
    datadoc = [=13=]
    if (match(datadoc,/file="([^-]+)-[^"]+.XML"/,a)) {
        close(fn)
        fn=a[1]".xml"
        print xmlversion ORS doctype ORS datadoc > fn
        print fn
        next
    }
}
cnt { cnt--; next }
{
    print > fn
    system("xmllint --debug --dtdvalid refcase.dtd 7" fn "7 > 7" fn ".rpt7")
}

现在我们可以看到您正在为输出的每一行调用 "xmllint",而不是为每个已完成的输出文件调用。将您的命令更改为:

/<?xml version/{
    xmlversion = [=14=]
    cnt = 3
}
cnt==2 {
    doctype = [=14=]
}
cnt==1 {
    if (match([=14=],/file="([^-]+)-[^"]+.XML"/,a)) {
        lint(fn)
        fn=a[1]".xml"
        print xmlversion ORS doctype ORS [=14=] > fn
        print fn
        next
    }
}
cnt { cnt--; next}
{ print > fn }
END { lint(fn) }

function lint(fn) {
    if (fn != "") {
        close(fn)
        system("xmllint --debug --dtdvalid refcase.dtd 7" fn "7 > 7" fn ".rpt7")
        fn = ""
    }
}

最后,鉴于我现在对您的预期输出的了解,这就是我真正编写脚本的方式(还修复了 <?xml 和 [=19= 中未转义的正则表达式元字符 ? ] 在 .XML 中我之前没有发现):

/<\?xml version/ {
    lint(fn)
    fn = ""
}
match([=15=],/file="([^-]+)-[^"]+\.XML"/,a) {
    fn = a[1]".xml"
    [=15=] = prev2 ORS prev1 ORS [=15=]
    print fn
}
{
    if ( fn != "" ) {
        print > fn
    }
    prev2 = prev1
    prev1 = [=15=]
}
END { lint(fn) }

function lint(fn) {
    if (fn != "") {
        close(fn)
        system("xmllint --debug --dtdvalid refcase.dtd 7" fn "7 > 7" fn ".rpt7")
    }
}