Como grep para 2 strings (condição AND) dentro de um bloco que não estão na mesma linha e, em seguida, encontrar algo dentro desse mesmo bloco

0

Eu queria saber como grep para dizer duas strings com condição AND dentro de um bloco se as strings não aparecerem na mesma linha. Eu tentei o seguinte, mas eles não funcionam para as seqüências de caracteres não na mesma linha: -

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

Por exemplo, eu tenho um arquivo xml com as seguintes linhas: -

  <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>

Observe que os dois blocos de código nas tags <test-result></test-result> diferem no caso de testpath . Então, quero grep para o logicalname e o result ( grep this.is.test1 AND FAILURE ) e localize o respectivo testpath para o mesmo bloco.

Em seguida, depois que eu tiver o testpath para o cenário FAILURE , como posso modificar o arquivo para gerar o resultado em SUCCESS para o bloco com o " testpath que encontrei" e o logicalname ?

    
por AJS 02.08.2017 / 11:47

2 respostas

0

Com a observação de que "analisar XML é uma prática ruim", aqui está uma solução awk para sua pergunta:

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

Acima, estamos dizendo awk que R ecord S eperador RS é <test-result , então para cada registro os dois PADRÕES aparecerão ( logicalname="this.is.test1" e result="FAILURE" ), se estiver lá (dentro de um mesmo bloco), altere FAILURE para SUCCESS do dado infile.txt

Como falamos nos comentários, uma vez que você deseja alterar o bloco específico com testpath=.... , você pode adicionar outra terceira condição apenas ao comando. abaixo mudará somente se testpath="file:/this/is/the/file/path1/abcd.xml" também for visto.

observe que você precisa escapar de / e é melhor também escapar de . 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
    
por 02.08.2017 / 12:47
2

Meu conselho é "Não se incomode em tentar fazer isso com grep " . Você poderia possivelmente criar um hack não baseado em regexp confiável em awk ou perl , mas as expressões regulares não podem ser confiavelmente usadas para analisar ou extrair dados do XML. E o que quer que você tenha criado provavelmente seria uma bagunça ilegível e inamovível. Existem maneiras melhores. Formas que realmente funcionam e funcionam de forma confiável.

Resumindo: não analise XML ou HTML com expressões regulares . não funciona .

Em vez disso, use um analisador xml como xmlstarlet . Como alternativa, use uma linguagem como perl ou python , ambas com várias bibliotecas de análise XML para escolher.

Se você realmente deseja processar XML com os favoritos da ferramenta orientada a linha grep (ou melhor ainda, awk ou perl ou mesmo sed ), primeiro converta o xml em um formato orientado a linha com xml2 . Esta não é uma opção ruim para a extração muito simples de dados de arquivos XML.

por exemplo. Depois de corrigir os erros mais óbvios com o xml de amostra, veja o que parece após o processamento com 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

Seria difícil conseguir o que você deseja usando apenas grep , mas bastante fácil com perl (apenas perl simples sem usar uma biblioteca XML) ou awk , e não muito difícil com sed .

Usando xmlstarlet ou uma biblioteca de análise de XML em perl ou python ou o que seria ainda mais fácil. Todos esses métodos trabalham diretamente com os dados estruturados em um documento XML, ou seja, lidam com cada elemento XML como um objeto distinto com atributos e valores selecionáveis, não apenas um grupo de linhas que podem estar de alguma forma conectadas.

BTW, existem várias perguntas com boas respostas sobre xmlstarlet e xml2 aqui neste site.

Tanto o xml2 quanto o xmlstarlet estão disponíveis pré-empacotados para a maioria das distribuições do Linux.

Por fim, tente começar com pelo menos XML razoavelmente bem formado. Seu exemplo de XML acima tem várias falhas. Entradas XML quebradas, incompletas ou abaixo do padrão serão muito difíceis de serem analisadas com qualquer ferramenta.

    
por 02.08.2017 / 12:17