使用源文件中的数据从 XML 文件中获取块
Get a block from an XML file using data from a source file
我修改了这个问题,因为我已经阅读了一些关于 XML 的内容。
我有一个包含 AuthNumbers 列表的文件源文件。
111222
111333
111444
etc.
我需要搜索该列表中的数字并在相应的 XML 文件中找到它们。
在 xml 文件中,该行的格式如下:
<trpcAuthCode>111222</trpcAuthCode>
这可以使用 grep 轻松实现,但是我需要包含事务的整个块。
块开始于:
<trans type="network sale" recalled="false">
或 <trans type="network sale" recalled="false" rollback="true">
and/or 一些其他变体。实际上 <trans*>
如果可能的话会更好。
块以</trans>
结尾
它不需要优雅或高效。我只需要它工作。我怀疑有些交易正在退出,我需要一种快速的方法来审查那些没有被处理的交易。
如果有帮助,这里是 link 原始(已消毒)xml
https://www.dropbox.com/s/cftn23tnz8uc9t8/main.xml?dl=0
以及我想提取的内容:
https://www.dropbox.com/s/b2bl053nom4brkk/transaction_results.xml?dl=0
每个结果的大小会有所不同,因为每笔交易的长度可能会有很大差异,具体取决于购买的产品数量。在结果 xml 中,您看到我根据 trpcAuthCode 列表 111222,111333,111444.
提取了我需要的 xml
关于 XML 和 awk 问题,您经常会发现 XML 处理的专家们的评论(他们的声誉中 k 的评论) awk 很复杂或不够用。据我了解这个问题,个人 and/or 调试目的需要该脚本。为此,我的解决方案应该足够了,但请记住,它不适用于任何合法的 XML 文件。
根据您的描述,脚本草图为:
匹配到<trans*>
开始录制
如果找到 <trpcAuthCode>
获取其内容并与列表进行比较。如果匹配,请记住输出块。
如果匹配到</trans>
则停止录制。如果输出已启用打印记录块否则丢弃它。
因为我在 中做了类似的事情,这应该不会太难实现。
不过,还需要一项附加功能:将 AuthNumbers 数组输入脚本。由于一个意外的巧合,我今天早上才在SO: How to access an array in an awk, which is declared in a different awk in shell? (thanks to the comment of jas]中得知答案。
所以,把它放在一个脚本中 filter-trpcAuthCode.awk
:
BEGIN {
record = 0 # state for recording
buffer = "" # buffer for recording
found = 0 # state for found auth code
# build temp. array from authCodes which has to be pre-defined
split(authCodes, list, "\n")
# build final array where values become keys
for (i in list) authCodeList[list[i]]
# for debugging: output of authCodeList
print "<!-- authCodeList:"
for (authCode in authCodeList) {
print authCode
}
print "-->"
}
/<trans( [^>]*)?>/ {
record = 1 # start recording
buffer = "" # clear buffer
found = 0 # reset state for found auth code
}
record {
buffer = buffer"\n"[=10=] # record line (if recording is enabled)
}
record && /<trpcAuthCode>/ {
# extract auth code
authCode = gensub(/^.*>([^<]*)<\/trpcAuthCode.*$/, "\1", "g")
# check whether auth code in authCodeList
found = authCode in authCodeList
}
/<\/trans>/ {
record = 0 # stop recording
# print buffer if auth code has been found
if (found) {
print buffer
}
}
备注:
我最初在 BEGIN
的 authCodes
上应用 split()
时遇到了困难。这会生成一个数组,其中拆分值使用枚举键存储。因此,我寻找一种解决方案来使值本身成为数组的键。 (否则,in
运算符不能用于搜索。)我在SO: Check if array contains value.
的接受答案中找到了一个优雅的解决方案
我将提议的模式 <trans*>
实现为 /<trans( [^>]*)?/
,它甚至会匹配 <trans>
(尽管 <trans>
似乎永远不会在没有属性的情况下发生)但是不是 <transSet>
.
buffer = buffer"\n"[=28=]
将当前行附加到先前的内容。 [=29=]
包含没有换行符的行。因此,它必须重新插入。我是怎么做到的,缓冲区以换行符开头,但最后一行没有结尾。考虑到 print buffer
在文本末尾添加换行符,这对我来说很好。或者,上面的代码片段可以替换为
buffer = buffer [=31=] "\n"
甚至
buffer = (buffer != "" ? buffer"\n" : "") [=32=]
.
(这是一个品味问题。)
过滤后的文件只是打印到标准输出通道。它可能被重定向到一个文件。考虑到这一点,我将 additional/debug 输出格式化为 XML 注释。
如果您对 awk 有点熟悉,您可能会注意到我的脚本中没有任何 next
语句。这是故意的。换句话说,规则的顺序是精心选择的,使得一行可能被所有规则连续processed/affected。 (我测试了一个极端情况:
<trans><trpcAuthCode>111222</trpcAuthCode></trans>
甚至这个都被正确处理了。)
为了简化测试,我添加了一个包装器 bash 脚本 filter-trpcAuthCode.sh
#!/usr/bin/bash
# uncomment next line for debugging
#set -x
# check command line arguments
if [[ $# -ne 2 ]]; then
echo "ERROR: Illegal number of command line arguments!"
echo ""
echo "Usage:"
echo $(basename [=11=]) " XML_FILE AUTH_CODES"
exit 1
fi
# call awk script
awk -v authCodes="$(cat <)" -f filter-xml-trpcAuthCode.awk ""
我针对您的示例文件 main.xml
and got four matching blocks. I was a little bit concerned about the output because in your sample output transaction_results.xml 测试了脚本(在 Windows 10 上的 cygwin 中使用 bash)只有三个匹配块。但是目视检查我的输出似乎是合适的。 (所有四个命中都包含一个匹配的 <trpcAuthCode>
元素。)
为了演示,我稍微减少了您的样本输入 sample.xml
:
<?xml version="1.0"?>
<transSet periodID="1" periodname="Shift" longId="2017-04-27" shortId="052" site="12345">
<trans type="periodClose">
<trHeader>
</trHeader>
</trans>
<printCashier>
<cashier sysid="7" empNum="07" posNum="101" period="11">A.Dude</cashier>
</printCashier>
<trans type="printCashier">
<trHeader>
<cashier sysid="7" empNum="07" posNum="101" period="11">A.Dude</cashier>
<posNum>101</posNum>
</trHeader>
</trans>
<trans type="journal">
<trHeader>
</trHeader>
</trans>
<trans type="network sale" recalled="false">
<trHeader>
<termMsgSN type="FINANCIAL" term="908">31054</termMsgSN>
</trHeader>
<trPaylines>
<trPayline type="sale" sysid="1" locale="DOLLAR">
<trpCardInfo>
<trpcAccount>1234567890123456</trpcAccount>
<trpcAuthCode>532524</trpcAuthCode>
</trpCardInfo>
</trPayline>
</trPaylines>
</trans>
<trans type="network sale" recalled="false">
<trHeader>
<termMsgSN type="FINANCIAL" term="908">31054</termMsgSN>
</trHeader>
<trPaylines>
<trPayline type="sale" sysid="1" locale="DOLLAR">
<trpPaycode mop="3" cat="1" nacstendercode="generic" nacstendersubcode="generic">CREDIT</trpPaycode>
<trpAmt>61.77</trpAmt>
<trpCardInfo>
<trpcAccount>2345678901234567</trpcAccount>
<trpcAuthCode>111222</trpcAuthCode>
</trpCardInfo>
</trPayline>
</trPaylines>
</trans>
<trans type="periodClose">
<trHeader>
<date>2017-04-27T23:50:17-04:00</date>
</trHeader>
</trans>
<endTotals>
<insideSales>445938.63</insideSales>
</endTotals>
</transSet>
对于其他示例输入,我只是将文本复制到一个文件中 authCodes.txt
:
111222
111333
111444
在示例会话中使用两个输入文件:
$ ./filter-xml-trpcAuthCode.sh
ERROR: Illegal number of command line arguments!
Usage:
filter-xml-trpcAuthCode.sh XML_FILE AUTH_CODES
$ ./filter-xml-trpcAuthCode.sh sample.xml authCodes.txt
<!-- authCodeList:
111222
111333
111444
-->
<trans type="network sale" recalled="false">
<trHeader>
<termMsgSN type="FINANCIAL" term="908">31054</termMsgSN>
</trHeader>
<trPaylines>
<trPayline type="sale" sysid="1" locale="DOLLAR">
<trpPaycode mop="3" cat="1" nacstendercode="generic" nacstendersubcode="generic">CREDIT</trpPaycode>
<trpAmt>61.77</trpAmt>
<trpCardInfo>
<trpcAccount>2345678901234567</trpcAccount>
<trpcAuthCode>111222</trpcAuthCode>
</trpCardInfo>
</trPayline>
</trPaylines>
</trans>
$ ./filter-xml-trpcAuthCode.sh main.xml authCodes.txt >output.txt
$
最后一个命令将输出重定向到一个文件 output.txt
,之后可能会对其进行检查或处理。
我修改了这个问题,因为我已经阅读了一些关于 XML 的内容。
我有一个包含 AuthNumbers 列表的文件源文件。
111222
111333
111444
etc.
我需要搜索该列表中的数字并在相应的 XML 文件中找到它们。
在 xml 文件中,该行的格式如下:
<trpcAuthCode>111222</trpcAuthCode>
这可以使用 grep 轻松实现,但是我需要包含事务的整个块。
块开始于:
<trans type="network sale" recalled="false">
或 <trans type="network sale" recalled="false" rollback="true">
and/or 一些其他变体。实际上 <trans*>
如果可能的话会更好。
块以</trans>
它不需要优雅或高效。我只需要它工作。我怀疑有些交易正在退出,我需要一种快速的方法来审查那些没有被处理的交易。
如果有帮助,这里是 link 原始(已消毒)xml https://www.dropbox.com/s/cftn23tnz8uc9t8/main.xml?dl=0
以及我想提取的内容: https://www.dropbox.com/s/b2bl053nom4brkk/transaction_results.xml?dl=0
每个结果的大小会有所不同,因为每笔交易的长度可能会有很大差异,具体取决于购买的产品数量。在结果 xml 中,您看到我根据 trpcAuthCode 列表 111222,111333,111444.
提取了我需要的 xml关于 XML 和 awk 问题,您经常会发现 XML 处理的专家们的评论(他们的声誉中 k 的评论) awk 很复杂或不够用。据我了解这个问题,个人 and/or 调试目的需要该脚本。为此,我的解决方案应该足够了,但请记住,它不适用于任何合法的 XML 文件。
根据您的描述,脚本草图为:
匹配到
<trans*>
开始录制如果找到
<trpcAuthCode>
获取其内容并与列表进行比较。如果匹配,请记住输出块。如果匹配到
</trans>
则停止录制。如果输出已启用打印记录块否则丢弃它。
因为我在
不过,还需要一项附加功能:将 AuthNumbers 数组输入脚本。由于一个意外的巧合,我今天早上才在SO: How to access an array in an awk, which is declared in a different awk in shell? (thanks to the comment of jas]中得知答案。
所以,把它放在一个脚本中 filter-trpcAuthCode.awk
:
BEGIN {
record = 0 # state for recording
buffer = "" # buffer for recording
found = 0 # state for found auth code
# build temp. array from authCodes which has to be pre-defined
split(authCodes, list, "\n")
# build final array where values become keys
for (i in list) authCodeList[list[i]]
# for debugging: output of authCodeList
print "<!-- authCodeList:"
for (authCode in authCodeList) {
print authCode
}
print "-->"
}
/<trans( [^>]*)?>/ {
record = 1 # start recording
buffer = "" # clear buffer
found = 0 # reset state for found auth code
}
record {
buffer = buffer"\n"[=10=] # record line (if recording is enabled)
}
record && /<trpcAuthCode>/ {
# extract auth code
authCode = gensub(/^.*>([^<]*)<\/trpcAuthCode.*$/, "\1", "g")
# check whether auth code in authCodeList
found = authCode in authCodeList
}
/<\/trans>/ {
record = 0 # stop recording
# print buffer if auth code has been found
if (found) {
print buffer
}
}
备注:
我最初在
BEGIN
的authCodes
上应用split()
时遇到了困难。这会生成一个数组,其中拆分值使用枚举键存储。因此,我寻找一种解决方案来使值本身成为数组的键。 (否则,in
运算符不能用于搜索。)我在SO: Check if array contains value. 的接受答案中找到了一个优雅的解决方案
我将提议的模式
<trans*>
实现为/<trans( [^>]*)?/
,它甚至会匹配<trans>
(尽管<trans>
似乎永远不会在没有属性的情况下发生)但是不是<transSet>
.
buffer = buffer"\n"[=28=]
将当前行附加到先前的内容。[=29=]
包含没有换行符的行。因此,它必须重新插入。我是怎么做到的,缓冲区以换行符开头,但最后一行没有结尾。考虑到print buffer
在文本末尾添加换行符,这对我来说很好。或者,上面的代码片段可以替换为
buffer = buffer [=31=] "\n"
甚至
buffer = (buffer != "" ? buffer"\n" : "") [=32=]
.
(这是一个品味问题。)过滤后的文件只是打印到标准输出通道。它可能被重定向到一个文件。考虑到这一点,我将 additional/debug 输出格式化为 XML 注释。
如果您对 awk 有点熟悉,您可能会注意到我的脚本中没有任何
next
语句。这是故意的。换句话说,规则的顺序是精心选择的,使得一行可能被所有规则连续processed/affected。 (我测试了一个极端情况:
<trans><trpcAuthCode>111222</trpcAuthCode></trans>
甚至这个都被正确处理了。)
为了简化测试,我添加了一个包装器 bash 脚本 filter-trpcAuthCode.sh
#!/usr/bin/bash
# uncomment next line for debugging
#set -x
# check command line arguments
if [[ $# -ne 2 ]]; then
echo "ERROR: Illegal number of command line arguments!"
echo ""
echo "Usage:"
echo $(basename [=11=]) " XML_FILE AUTH_CODES"
exit 1
fi
# call awk script
awk -v authCodes="$(cat <)" -f filter-xml-trpcAuthCode.awk ""
我针对您的示例文件 main.xml
and got four matching blocks. I was a little bit concerned about the output because in your sample output transaction_results.xml 测试了脚本(在 Windows 10 上的 cygwin 中使用 bash)只有三个匹配块。但是目视检查我的输出似乎是合适的。 (所有四个命中都包含一个匹配的 <trpcAuthCode>
元素。)
为了演示,我稍微减少了您的样本输入 sample.xml
:
<?xml version="1.0"?>
<transSet periodID="1" periodname="Shift" longId="2017-04-27" shortId="052" site="12345">
<trans type="periodClose">
<trHeader>
</trHeader>
</trans>
<printCashier>
<cashier sysid="7" empNum="07" posNum="101" period="11">A.Dude</cashier>
</printCashier>
<trans type="printCashier">
<trHeader>
<cashier sysid="7" empNum="07" posNum="101" period="11">A.Dude</cashier>
<posNum>101</posNum>
</trHeader>
</trans>
<trans type="journal">
<trHeader>
</trHeader>
</trans>
<trans type="network sale" recalled="false">
<trHeader>
<termMsgSN type="FINANCIAL" term="908">31054</termMsgSN>
</trHeader>
<trPaylines>
<trPayline type="sale" sysid="1" locale="DOLLAR">
<trpCardInfo>
<trpcAccount>1234567890123456</trpcAccount>
<trpcAuthCode>532524</trpcAuthCode>
</trpCardInfo>
</trPayline>
</trPaylines>
</trans>
<trans type="network sale" recalled="false">
<trHeader>
<termMsgSN type="FINANCIAL" term="908">31054</termMsgSN>
</trHeader>
<trPaylines>
<trPayline type="sale" sysid="1" locale="DOLLAR">
<trpPaycode mop="3" cat="1" nacstendercode="generic" nacstendersubcode="generic">CREDIT</trpPaycode>
<trpAmt>61.77</trpAmt>
<trpCardInfo>
<trpcAccount>2345678901234567</trpcAccount>
<trpcAuthCode>111222</trpcAuthCode>
</trpCardInfo>
</trPayline>
</trPaylines>
</trans>
<trans type="periodClose">
<trHeader>
<date>2017-04-27T23:50:17-04:00</date>
</trHeader>
</trans>
<endTotals>
<insideSales>445938.63</insideSales>
</endTotals>
</transSet>
对于其他示例输入,我只是将文本复制到一个文件中 authCodes.txt
:
111222
111333
111444
在示例会话中使用两个输入文件:
$ ./filter-xml-trpcAuthCode.sh
ERROR: Illegal number of command line arguments!
Usage:
filter-xml-trpcAuthCode.sh XML_FILE AUTH_CODES
$ ./filter-xml-trpcAuthCode.sh sample.xml authCodes.txt
<!-- authCodeList:
111222
111333
111444
-->
<trans type="network sale" recalled="false">
<trHeader>
<termMsgSN type="FINANCIAL" term="908">31054</termMsgSN>
</trHeader>
<trPaylines>
<trPayline type="sale" sysid="1" locale="DOLLAR">
<trpPaycode mop="3" cat="1" nacstendercode="generic" nacstendersubcode="generic">CREDIT</trpPaycode>
<trpAmt>61.77</trpAmt>
<trpCardInfo>
<trpcAccount>2345678901234567</trpcAccount>
<trpcAuthCode>111222</trpcAuthCode>
</trpCardInfo>
</trPayline>
</trPaylines>
</trans>
$ ./filter-xml-trpcAuthCode.sh main.xml authCodes.txt >output.txt
$
最后一个命令将输出重定向到一个文件 output.txt
,之后可能会对其进行检查或处理。