【问题标题】:bash using awk or sed to search backwards from occurance to a specific stringbash 使用 awk 或 sed 向后搜索从出现到特定字符串
【发布时间】:2016-11-16 17:54:36
【问题描述】:

我有一个 xml 文件,正在此文件中搜索一个字符串。 一旦(如果)找到字符串,我需要能够搜索回另一个字符串的位置并输出数据。

即:

<xml>
<packet>
 <proto>
 <field show="bob">
 </proto>
</packet>
<packet>
 <proto>
 <field show="rumpelstiltskin">
 </proto>
</packet>
<packet>
 <proto>
 <field show="peter">
 </proto>
</packet>

我的输入是已知的:

show="rumpelstiltskin" 

<packet>

我需要得到以下结果(基本上是第二个块);

<packet>
<proto>
<field show="rumpelstiltskin">
</proto>
</packet>

<packet>
<proto>
<field show="rumpelstiltskin">

有数千个(wireshark pdml 转换),show="rumpelstilstkin" 可以出现在文件中的任何位置,并且该部分可以是任意大小。

我以前做过这个,我很确定这可以在 awk 或 sed oneliner 中实现。任何帮助表示赞赏!

【问题讨论】:

    标签: bash search awk sed


    【解决方案1】:

    您需要将您的 XML 视为 XML 并使用适当的工具。例如,稍微修改您的 XML 以使其有效:

    <xml>
      <packet>
        <proto>
          <field show="bob"/>
        </proto>
      </packet>
      <packet>
        <proto>
          <field show="rumpelstiltskin"/>
        </proto>
      </packet>
      <packet>
        <proto>
          <field show="peter"/>
        </proto>
      </packet>
    </xml>
    

    你可以像这样使用xmllint

    xmllint --xpath '//packet[proto/field/@show="rumpelstiltskin"]' file.xml
    

    这匹配并打印在&lt;proto&gt; 元素中包含&lt;field show="rumpelstiltskin"&gt; 的所有&lt;packet&gt; 元素的内容。

    如果你不想指定完整的层次结构,你可以使用类似这样的东西:

    xmllint --xpath '//packet[descendant::field[@show="rumpelstiltskin"]]' file.xml
    

    【讨论】:

    • 不错。另一个XML处理工具:xmlstarlet sel -t -c '//packet[descendant::field[@show="'"$show"'"]]' file.xml
    • 绝对是我想要的方式,但是,xmllint 出现此错误不确定它是否喜欢大文件.. # xmllint --xpath '//packet[descendant::field[@ show="12192620160920141757196"]]' file.pdml Unknown option --xpath [code] # xmllint '//packet[descendant::field[@show="12192620160920141757196"]]' file.pdml 警告:加载外部实体失败"/packet[descendant::field[@show="12192620160920141757196"]]" 被杀死 [/code]
    • 嗯,这个错误看起来更像是不支持 --xpath 选项,这听起来很奇怪。不确定它是否相关,但您的命令中似乎有 ""
    • 是的,我猜它是旧版本。稍后测试,谢谢! # xmllint --version xmllint: using libxml version 20706 compiled with: Threads Tree Output Push Reader Patterns Writer SAXv1 FTP HTTP DTDValid HTML Legacy C14N Catalog XPath XPointer XInclude Iconv ISO8859X Unicode Regexps Automata Expr Schemas Schematron Modules Debug Zlib # cat /etc/redhat-release Red Hat Enterprise Linux Server release 6.6 (Santiago) 是的,更正了“”也......仍然是同样的问题。
    • @TheHCD 看起来像 the issue is covered here
    【解决方案2】:

    你可以用 grep 做到这一点

    cat file | grep 'show="rumpelstiltskin"' -B5 | grep 'otherstring'

    显然将-B5 调整为您需要保留要查找的字符串的行数。

    【讨论】:

    • 是的,正在考虑这个.. 甚至 tac 使用 awk 但在 行高度可变的某些情况下它并不准确。基本上它是一个转换后的wireshark跟踪,我只需要获取出现字符串的特定帧的帧号。
    【解决方案3】:

    所以......你可以一起破解一些东西,将你的文件基本解析为文本文件......

    awk -v txt="rumpel" '$0=="<packet>"{s=$0; found=0; next} $0~txt{found=1} {s=s RS $0} $0=="</packet>" && found {print s}' inp.xml
    

    为了便于解释,分成几部分,它执行以下操作:

    • -v txt="rumpel" - 设置一个在脚本中使用的变量。请注意,在此示例中,这将被评估为正则表达式,但如果您更喜欢将其作为字符串进行搜索,则可以使用 index()
    • $0=="&lt;packet&gt;"{s=$0; found=0; next} - 如果我们找到数据包的开头,请重置我们的存储变量 (s) 和标志 (found)。
    • $0~txt{found=1} - 如果我们找到了我们正在寻找的文本,设置一个标志。
    • {s=s RS $0} - 将当前行附加到变量中,并且
    • $0=="&lt;/packet&gt;" &amp;&amp; found {print s} - 如果我们在文本的末尾并且找到了字符串,则打印。

    一种更好的方法可能是使用本机理解 XML 的东西来解释 XML,但仅使用 sed 和 awk 是不可能的。

    【讨论】:

    • 甜蜜!有用。但是,想知道如何在找到搜索 txt 字符串的第一次出现后停止它,而不是通过 200M 文件击败它。而且,是的,我尝试过使用 xmllint,但我认为 wireshark pdml 输出格式与它......(至少我没有让它工作)
    • 是的,您发布的示例确实是无效的 XML。 &lt;field&gt; 没有关闭。应该是&lt;field show="yadda" /&gt;。此外,没有关闭&lt;/xml&gt;。在找到第一个记录后停止的一种简单方法是在print s 之后添加;exit。或者,如果您在一个命令行上处理多个文件并且您的 awk 支持它,;nextfile.
    【解决方案4】:

    如果您的输入真的那么简单,您只需要:

    $ awk '/<packet>/{buf=""} {buf=buf $0 RS} /rumpelstiltskin/{printf "%s",buf}' file
    <packet>
     <proto>
     <field show="rumpelstiltskin">
    

    或者如果您愿意:

    $ awk '/<packet>/{buf="";f=0} {buf=buf $0 RS} /rumpelstiltskin/{f=1} f&&/<\/packet>/{printf "%s",buf}' file
    <packet>
     <proto>
     <field show="rumpelstiltskin">
     </proto>
    </packet>
    

    如果您想在第一次打印后停止读取输入文件,则只需在其后添加;exit,以便printf "%s",buf 变为printf "%s",buf; exit

    【讨论】:

      【解决方案5】:

      这可能对你有用(GNU sed):

      sed '/<packet>/h;//!H;/rumpelstiltskin/!d;x;q' file
      

      这会将所需的字符串存储在保持空间中,打印出来并退出。

      但是要确保第一个和第二个字符串存在并且彼此相邻:

      sed '/<packet>/h;//!H;/rumpelstiltskin/!d;x;/<packet>.*rumpelstiltskin/!d;q' file
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-21
        • 2018-11-24
        • 1970-01-01
        • 2019-08-07
        • 2016-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多