Correspondência de padrões multilinha usando sed, awk ou grep

25

É possível fazer uma correspondência de padrão de várias linhas usando sed , awk ou grep ? Por exemplo, gostaria de obter todas as linhas entre { e }

Por isso, deve ser capaz de corresponder

 1. {}
 2. {.....}
 3. {.....
.....}

Inicialmente, a pergunta usou <p> como exemplo. Editou a pergunta para usar { e } .

    
por jasonwryan 28.03.2011 / 13:15

5 respostas

19

Embora eu concorde com o conselho acima, que você queira obter um analisador para algo mais do que minúsculo ou completamente ad-hoc, é (apenas ;-) possível combinar blocos de várias linhas entre chaves com sed .

Aqui está uma versão de depuração do código sed

sed -n '/[{]/,/[}]/{
    p
    /[}]/a\
     end of block matching brace

    }' *.txt

Algumas notas,

  • -n significa 'nenhuma linha de impressão padrão como processada'.
  • 'p' significa que agora imprime a linha.
  • A construção /[{]/,/[}]/ é uma expressão de intervalo. Isso significa verificar até encontrar algo que corresponda ao primeiro padrão (/[{]/) AND, em seguida, varrer até encontrar o segundo padrão (/[}]/) THEN executar qualquer ação que você encontrar entre o {} no código sed. Neste caso, 'p' e o código de depuração. (não explicado aqui, use-o, mod-lo ou retirá-lo como funciona melhor para você).

Você pode remover a depuração / [}] / a \ end do bloco quando provar que o código está realmente correspondendo aos blocos delimitados por {,}.

Este exemplo de código irá ignorar qualquer coisa que não esteja dentro de um par de chaves. Ele será, como observado por outras pessoas acima, facilmente confundido se você tiver qualquer {,} embutido em strings, reg-exps, etc., OU onde a chave de fechamento é a mesma linha , ( obrigado a fred.bear)

Espero que isso ajude.

    
por 29.03.2011 / 00:01
13

Você pode usar a opção -M (multiline) para pcregrep:

pcregrep -M '\{(\s*.*\s*)*\}' test.txt

\ s é espaço em branco (incluindo novas linhas), portanto, isso corresponde a zero ou mais ocorrências de (espaços em branco seguidos por. * seguidos por espaço em branco), todos entre chaves.

Atualização:

Isso deve fazer a correspondência não desejada:

pcregrep -n -M '\{(\n*.*?\n*)*?\}' test.txt
    
por 28.03.2011 / 23:05
5

Expressões semelhantes a XML (tags infinitamente recursivas) não são uma 'linguagem regular', portanto, não podem ser analisadas com expressões regulares (regex). Aqui está o porquê:

link

link

link

    
por 28.03.2011 / 14:30
5

parser.awk:

#!/usr/bin/awk -f    
function die(msg) { print msg > "/dev/stderr"; exit 1 }
BEGIN {
  FS=opener
  if (mode=="l") linewise=1
  else if (mode=="i") trim_closer=length(closer)
  else if (mode!="a") die("mode must be one of: l,i,a")
}
{
  live=level
  for (f=1; f<=NF; f++) {
    if (f>1) {
      live=++level
      if (mode=="i" && level>1 || mode=="a") printf "%s", opener
    }
    cur=$f
    level-=gsub(closer, "", cur)
    if (level<0) die("Unbalanced")
    if (!linewise) {
      cur=$f
      if (sub(".*" closer, "", cur)) printf "%s", 
        substr($f, 1, length($f) - length(cur) - (level ? 0 : trim_closer))
      else if (live) printf "%s", $f
    }
  }
  if (live) {
    if (linewise) print
    else print ""
  }
}
END { if (level>0) die("Unbalanced") }

Chame como awk -v'opener={' -v'closer=}' -v'mode=a' -f parser.awk . Se o modo for a , ele imprimirá os colchetes e o conteúdo de todos os mais externos e equilibrados {...} ; se o modo for i , ele imprimirá apenas o conteúdo deles; se o modo for l , ele imprime linhas completas onde um {...} mais externo começa, permanece aberto ou é fechado.

    
por 20.04.2012 / 01:45
1

Expressões regulares não podem encontrar parênteses aninhados correspondentes.

Se tiver certeza de que não haverá parênteses aninhados dentro do que você está pesquisando, será possível pesquisar até o primeiro fechamento. Por exemplo:

sed -r 's#\{([^}])\}##'

Isso substituirá todo o texto de '{' a '}' pelo que há entre eles.

    
por 28.03.2011 / 22:16