使用源文件中的数据从XML文件中获取块

时间:2017-04-26 19:05:11

标签: xml awk sed grep pcregrep

我修改了这个问题,因为我已经阅读了一些关于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。

1 个答案:

答案 0 :(得分:0)

关于XML和awk问题,您经常会发现大师的评论(如果他们的声誉中存在 k ),则awk中的XML处理复杂或不充分。正如我理解的那样,脚本是出于个人和/或调试目的而需要的。为此,我的解决方案应该足够了,但请记住,它不适用于任何合法的XML文件。

根据您的描述,脚本的草图是:

  1. 如果<trans*>匹配,则开始录制。

  2. 如果找到<trpcAuthCode>,请获取其内容并与列表进行比较。如果匹配,请记住阻止输出。

  3. 如果匹配</trans>则停止录制。如果已启用输出,则打印已记录的块,否则将其丢弃。

  4. 因为我在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
      }
    }
    

    注意:

    1. 我在split() authCodes BEGIN上应用in时最初挣扎。这使得一个数组中的分割值与枚举键一起存储。因此,我寻找一种解决方案,使值本身成为数组的键。 (否则,<trans*>运算符不能用于搜索。)我在SO: Check if array contains value的接受答案中找到了一个优雅的解决方案。

    2. 我将提议的模式/<trans( [^>]*)?/实现为<trans>,它甚至会匹配<trans>(尽管<transSet>似乎永远不会在没有属性的情况下发生)但不是{{1} }}


    3. buffer = buffer"\n"$0
      将当前行附加到先前的内容。 $0包含没有换行符的行。因此,必须重新插入。我是怎么做到的,缓冲区以换行符开头但最后一行没有结束。考虑到print buffer在文本末尾添加换行符,这对我来说很好。或者,上述片段可以由替换 buffer = buffer $0 "\n"
      甚至是 buffer = (buffer != "" ? buffer"\n" : "") $0
      (这是品味问题。)

    4. 过滤后的文件只是打印到标准输出通道。它可能会被重定向到一个文件。考虑到这一点,我将附加/调试输出格式化为XML注释。

    5. 如果您对awk有点熟悉,您可能会注意到我的脚本中没有任何next语句。这是故意的。换句话说,规则的顺序是精心选择的,以便所有规则可以连续地处理/影响一条线。 (我测试了一个极端情况:
      <trans><trpcAuthCode>111222</trpcAuthCode></trans>
      甚至这是正确处理的。)

    6. 为了简化测试,我添加了一个包装器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,然后可以对其进行检查或处理。