我修改了这个问题,因为我已经阅读了一些关于XML的内容。
我有一个包含AuthNumbers列表的文件源文件。
<trpcAuthCode>111222</trpcAuthCode>
我需要搜索该列表中的数字,并在相应的XML文件中找到它们。
在xml文件中,行的格式如下:
<trans type="network sale" recalled="false">
使用grep可以非常轻松地实现这一点,但是我需要包含事务的整个块。
该块以:
<trans type="network sale" recalled="false" rollback="true">
或<trans*>
和/或其他一些变体。实际上</trans>
如果可能的话,那将是最好的。
该块以{{1}}
结尾它不需要优雅或高效。我只是需要它才能工作。我怀疑一些交易正在退出,我需要一种快速方法来审查那些没有被处理的交易。
如果它有帮助,这里是原始(灭菌)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。
答案 0 :(得分:0)
关于XML和awk问题,您经常会发现大师的评论(如果他们的声誉中存在 k ),则awk中的XML处理复杂或不充分。正如我理解的那样,脚本是出于个人和/或调试目的而需要的。为此,我的解决方案应该足够了,但请记住,它不适用于任何合法的XML文件。
根据您的描述,脚本的草图是:
如果<trans*>
匹配,则开始录制。
如果找到<trpcAuthCode>
,请获取其内容并与列表进行比较。如果匹配,请记住阻止输出。
如果匹配</trans>
则停止录制。如果已启用输出,则打印已记录的块,否则将其丢弃。
因为我在SO: Shell scripting - split xml into multiple files做了类似的事情,所以这应该不会太难以实现。
但是,还需要一个额外的功能:将AuthNumbers数组提供给脚本。由于令人惊讶的巧合,我今天早上在SO: How to access an array in an awk, which is declared in a different awk in shell?中得到了答案(感谢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"$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
}
}
注意:
我在split()
authCodes
BEGIN
上应用in
时最初挣扎。这使得一个数组中的分割值与枚举键一起存储。因此,我寻找一种解决方案,使值本身成为数组的键。 (否则,<trans*>
运算符不能用于搜索。)我在SO: Check if array contains value的接受答案中找到了一个优雅的解决方案。
我将提议的模式/<trans( [^>]*)?/
实现为<trans>
,它甚至会匹配<trans>
(尽管<transSet>
似乎永远不会在没有属性的情况下发生)但不是{{1} }}
在
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
,然后可以对其进行检查或处理。