我修改了这个问题,因为我一直在阅读一些关于XML的内容。
我有一个包含身份验证编号列表的文件源文件。
111222
111333
111444
etc.
我需要在该列表中搜索数字并在相应的 XML 文件中找到它们。 在 xml 文件中,该行的格式如下:<trpcAuthCode>111222</trpcAuthCode>
这可以使用 grep 轻松实现,但我需要包含交易的整个块。
该块以以下内容开头:<trans type="network sale" recalled="false">
或<trans type="network sale" recalled="false" rollback="true">
和/或其他一些变体。实际上,如果这样的事情是可能的,<trans*>
将是最好的。
块以</trans>
结尾
它不需要优雅或高效。我只需要它来工作。我怀疑一些交易正在退出,我需要一种快速的方法来审查未处理的交易。
如果有帮助,这里是指向原始(灭菌)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问题,您经常会发现大师们的评论(如果他们的声誉为k的话)评论说 awk 中的 XML 处理很复杂或不够。据我了解,该脚本用于个人和/或调试目的。为此,我的解决方案应该足够了,但请记住,它不适用于任何合法的XML文件。
根据您的描述,脚本的草图为:
如果
<trans*>
匹配,请开始录制。如果找到
<trpcAuthCode>
则获取其内容并与列表进行比较。在匹配的情况下,请记住输出块。如果匹配
</trans>
则停止录制。如果已启用输出,则打印记录的块,否则将其丢弃。
因为我在 SO 中做了类似的事情:Shell 脚本 - 将 xml 拆分为多个文件,这应该不会变得太难实现。
但是,还需要一个额外的功能:将 AuthNumbers 数组馈送到脚本中。由于一个令人惊讶的巧合,我今天早上在 SO 中得知了答案:如何访问 awk 中的数组,该数组在 shell 中的不同 awk 中声明?(感谢贾斯的评论)。
因此,将其完全放在脚本中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"$0 # 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 的公认答案中找到了一个优雅的解决方案:检查数组是否包含值。我将提议的模式
<trans*>
实现为/<trans( [^>]*)?/
,它甚至可以匹配<trans>
(尽管<trans>
似乎永远不会在没有属性的情况下发生),但不是<transSet>
。buffer = buffer"n"$0
将当前行追加到前面的内容中。$0
包含不带换行符的行。因此,它必须重新插入。我是怎么做到的,缓冲区以换行符开头,但最后一行以换行符结尾。考虑到print buffer
在文本末尾添加了换行符,这对我来说很好。或者,上面的代码片段可以替换为buffer = buffer $0 "n"
甚至buffer = (buffer != "" ? buffer"n" : "") $0
。
(这是一个品味问题。过滤后的文件只需打印到标准输出通道。它可能被重定向到文件。考虑到这一点,我将附加/调试输出格式化为 XML 注释。
如果你对awk有点熟悉,你可能会注意到我的脚本中没有任何
next
语句。这是有意为之。换句话说,规则的顺序是精心选择的,因此所有规则可以连续处理/影响一行。(我测试了一个极端情况:<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 $0) " XML_FILE AUTH_CODES"
exit 1
fi
# call awk script
awk -v authCodes="$(cat <$2)" -f filter-xml-trpcAuthCode.awk "$1"
我针对您的示例文件main.xml
测试了脚本(在 Windows 10 上的 cygwin 中使用 bash),并得到了四个匹配的块。我有点担心输出,因为在您的示例输出中,transaction_results.xml只有三个匹配的块。但是目视检查我的输出似乎是合适的。(所有四个命中都包含一个匹配的<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
之后可能会对其进行检查或处理。