같은 줄(AND 조건)에 있지 않은 블록 내에서 2개의 문자열을 찾은 다음 동일한 블록 내에서 다른 내용을 찾는 방법

같은 줄(AND 조건)에 있지 않은 블록 내에서 2개의 문자열을 찾은 다음 동일한 블록 내에서 다른 내용을 찾는 방법

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>있으므로 동일한 블록에 해당하는 값을 AND ( AND ) 로 찾고 testpath싶습니다 .greplogicalnameresultgrep this.is.test1FAILUREtestpath

testpath다음으로, 장면의 가 있으면 결과가 " 내가 찾았습니다" 및 ? 에 FAILURE맞도록 파일을 어떻게 수정합니까 ?SUCCESStestpathlogicalname

답변1

내 제안은“그럴려고 애쓰지도 마세요 grep. awk또는 에서 일부 이상한 정규식 기반 해킹을 제거 할 수 있지만 perl정규식은 그렇지 않습니다.안정적으로XML에서 데이터를 구문 분석하거나 추출하는 데 사용됩니다. 무엇을 생각해내든 읽을 수 없고 유지 관리도 불가능한 엉망이 될 가능성이 높습니다. 더 좋은 방법이 있습니다. 실용적이고 효과적이며 신뢰할 수 있는 방법입니다.

즉, XML이나 HTML을 구문 분석하지 마세요.정규 표현식 사용. 그것작동하지 않습니다.

대신 다음과 같은 XML 파서를 사용하세요.xmlstarlet. 또는 선택할 수 있는 여러 XML 구문 분석 라이브러리가 있는 perl또는 같은 언어를 사용하세요.python

grep줄 기반 도구 (또는 더 나은 방법 awk, perl심지어 ) 를 사용하여 XML을 처리하려면 sed먼저 다음을 사용하여 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(XML 라이브러리를 사용하지 않고 그냥 평범한 Perl) awk사용하기에도 그리 어렵지 않습니다 sed.

또는 에서 XML 구문 분석 라이브러리를 사용하는 것이 xmlstarlet더 쉽습니다 . 이러한 모든 방법은 XML 문서의 구조화된 데이터와 직접 작동합니다. 즉, 각 XML 요소는 어떤 방식으로든 연결될 수 있는 행 묶음이 아니라 선택적 속성과 값이 있는 고유한 개체로 처리됩니다.perlpython

그런데 좋은 답변이 있는 질문이 많아요xmlstarlet그리고XML2이 웹사이트에서.

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말한다오른쪽에코코드에스연산자 는 다음과 RS같습니다 <test-result. 각 레코드에 대해 두 개의 패턴( logicalname="this.is.test1"result="FAILURE")이 검색되고 해당 패턴이 있으면(동일 블록 내에서) FAILURE지정된 SUCCESS패턴이 다음으로 변경됩니다 .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

관련 정보