如何 grep 查找块内不在同一行的 2 个字符串(AND 条件),然后在同一块内查找其他内容

如何 grep 查找块内不在同一行的 2 个字符串(AND 条件),然后在同一块内查找其他内容

grep我想知道如果字符串不出现在同一行,我们如何在块内使用 AND 条件表示两个字符串。我尝试了以下方法,但它们不适用于不在同一行的字符串:-

  1. grep 'string1.*string2\|string2.*string1' filename
  2. grep -P '^(?=.*pattern1)(?=.*pattern2)' filename

例如我有一个包含以下几行的 xml 文件:-

  <test-result
    exectime="2017-07-07"
    result="FAILURE"
    isdone="TRUE"
    logicalname="this.is.test1"
    duration="10050"
  >
    <test-case
      testcasename="this.is.test.case.name1"
      testunit="abcd-mc"
      testpath="file:/this/is/the/file/path1/abcd.xml"
     >
     </test-case>

    </test-result>

 <test-result
      exectime="2017-07-07"
      result="SUCCESS"
      isdone="TRUE"
      logicalname="this.is.test1"
      duration="10050"
     >
    <test-case
     testcasename="this.is.test.case.name1"
     testunit="abcd-mc"
     testpath="file:/this/is/the/file/path1/uvwx.xml"
    >

   </test-case>
  </test-result>

请注意,标签内的 2 个代码块<test-result></test-result>testpath.所以,我想greplogicalnameresult( grep this.is.test1AND FAILURE) 找到testpath同一个块的相应值。

接下来,一旦我有了testpath场景的FAILURE,我如何修改文件以使结果适合SUCCESS带有“testpath我找到的”和logicalname

答案1

我的建议是“甚至不用费心尝试这样做grep。您可能会在awkor中敲出一些不可靠的基于正则表达式的 hack perl,但正则表达式不能可靠地用于从 XML 中解析或提取数据。无论你想到什么,很可能都是一团难以阅读和难以维护的混乱。还有更好的方法。实际有效且可靠的方法。

简而言之:不要解析 XML 或 HTML用正则表达式。它不起作用

相反,使用 xml 解析器,例如xmlstarlet。或者,使用类似perl或 的语言python,这两种语言都有多个 XML 解析库可供选择。

如果您确实想使用面向行的工具grep(或者更好的是awkor perl,甚至sed)处理 XML,请首先使用以下命令将 xml 转换为面向行的格式:XML2。对于从 XML 文件中非常简单地提取数据来说,这是一个不错的选择。

例如,在修复了示例 xml 中最明显的错误后,以下是使用以下命令处理后的样子xml2

$ xml2 < ajs.xml 
/xml/test-result/@exectime=2017-07-07
/xml/test-result/@result=FAILURE
/xml/test-result/@isdone=TRUE
/xml/test-result/@logicalname=this.is.test1
/xml/test-result/@duration=10050
/xml/test-result/test-case/@testcasename=this.is.test.case.name1
/xml/test-result/test-case/@testunit=abcd-mc
/xml/test-result/test-case/@testpath=file:/this/is/the/file/path1/abcd.xml
/xml/test-result
/xml/test-result/@exectime=2017-07-07
/xml/test-result/@result=SUCCESS
/xml/test-result/@isdone=TRUE
/xml/test-result/@logicalname=this.is.test1
/xml/test-result/@duration=10050
/xml/test-result/test-case/@testcasename=this.is.test.case.name1
/xml/test-result/test-case/@testunit=abcd-mc
/xml/test-result/test-case/@testpath=file:/this/is/the/file/path1/uvwx.xml

仅使用 很难获得您想要的东西grep,但是使用perl(只需简单的 perl 而不使用 XML 库)或相当容易awk,并且使用 也不太困难sed

使用或xmlstarlet中的 XML 解析库会更容易。所有这些方法都直接处理 XML 文档中的结构化数据,即将每个 XML 元素作为具有可选属性和值的不同对象来处理,而不仅仅是一堆可能以某种方式连接的行。perlpython

顺便说一句,有很多问题都有很好的答案xmlstarletXML2在这个网站上。

xml2和都xmlstarlet针对大多数 Linux 发行版进行了预打包。

最后,尝试从至少结构合理的 XML 开始。上面的示例 XML 有几个缺陷。任何工具都很难解析损坏的、不完整的或不符合标准的 XML 输入。

答案2

注意到“解析 XML 是不好的做法”,这里是awk您问题的解决方案:)

awk -v RS="<test-result" '
    /logicalname="this\.is\.test1"/&&/result="FAILURE"/ {
    sub("FAILURE","SUCCESS")
}1' RS='' infile.txt

在上面,我们告诉awk我们埃科德SeperatorRS<test-result,那么对于每个记录,将查找两个模式(logicalname="this.is.test1"result="FAILURE"),如果它在那里(在同一个块内),则从FAILURESUCCESS定的更改为infile.txt

正如我们在评论中所说,因为您想使用 更改特定块testpath=....,您可以仅向命令添加另一个第三个条件。仅当也看到时,下面才会改变testpath="file:/this/is/the/file/path1/abcd.xml"

请注意,您需要 escape /,并且最好也 escape .s 。

awk -v RS="<test-result" '  /logicalname="this\.is\.test1"/&&/result="FAILURE"/&&/testpath="file:\/this\/is\/the\/file\/path1\/abcd\.xml"/
    {sub("FAILURE","SUCCESS")
}1' RS='' infile.txt

相关内容