Filtrar parágrafos separados de acordo com a primeira palavra?

2

Eu tenho um programa que imprime linhas de texto ("parágrafos") separados por '-'. Por exemplo, pode imprimir

--
are you happy
--
I am hungry
are you
--
are(you hungry
too

Eu quero canalizar isso em outro programa (sed talvez?) e voltar apenas os parágrafos que começam com uma determinada palavra (por exemplo, "são"). Então, no caso acima, recebendo parágrafos que começam com "estão" de volta eu recebo

--
are you happy
--
are(you hungry
too

O programa imprime um número potencialmente grande de "parágrafos", mas espero que apenas um pequeno número corresponda, e é por isso que eu preferiria poder filtrar a saída do programa de maneira contínua (evitando gravar tudo em um arquivo arquivo enorme e depois filtrá-lo).

    
por LangeHaare 20.07.2018 / 17:54

4 respostas

3

AWK

Usando o GNU awk ou mawk:

$ awk '$1~"^"word{printf("--\n%s",$0)}' word='are' RS='--\n' infile
--
are you happy
--
are(you hungry
too

Isto ajusta a palavra variável para a palavra correspondente no início do registro e RS (separador de registro) para '-' seguido por uma nova linha \n . Em seguida, para qualquer registro que comece com a palavra correspondente ( $1~"^"word ), imprima um registro formatado. O formato é um começo '-' com uma nova linha com o registro exato encontrado.

GREP

Usando (GNU para a opção -z ) grep:

grep -Pz -- '--\nare(?:[^\n]*\n)+?(?=--|\Z)' infile
grep -Pz -- '(?s)--\nare.*?(?=\n--|\Z)\n' infile
grep -Pz -- '(?s)--\nare(?:(?!\n--).)*\n' infile

Descrição (ões) Para as descrições a seguir, a opção (?x) da PCRE é usada para adicionar (muitos) comentários explicativos (e espaços) em linha com a regex real (em funcionamento). Se os comentários (e a maioria dos espaços) (até a próxima nova linha) forem removidos, a string resultante ainda será a mesma regex. Isso permite a descrição da regex em detalhes no código de trabalho. Isso facilita muito a manutenção do código.

Opção 1 regex (?x)--\nare(?:[^\n]*\n)+?(?=--|\Z)

(?x)   # match the remainder of the pattern with the following
       # effective flags: x
       #      x modifier: extended. Spaces and text after a # 
       #      in the pattern are ignored
--     # matches the characters -- literally (case sensitive)
\n     # matches a line-feed (newline) character (ASCII 10)
are    # matches the characters are literally (case sensitive)
(?:    #      Non-Capturing Group (?:[^\n]*\n)+?
[^\n]  #           matches non-newline characters
*      #           Quantifier — Matches between zero and unlimited times, as
       #           many times as possible, giving back as needed (greedy)
\n     #           matches a line-feed (newline) character (ASCII 10)
)      #      Close the Non-Capturing Group
+?     # Quantifier — Matches between one and unlimited times, as
       # few times as possible, expanding as needed (lazy)
       # A repeated capturing group will only capture the last iteration.
       # Put a capturing group around the repeated group to capture all
       # iterations or use a non-capturing group instead if you're not
       # interested in the data
(?=    # Positive Lookahead (?=--|\Z)
       # Assert that the Regex below matches
       #      1st Alternative --
--     #           matches the characters -- literally (case sensitive)
|      #      2nd Alternative \Z
\Z     #           \Z asserts position at the end of the string, or before
       #           the line terminator right at the end of the 
       #           string (if any)
)      #      Closing the lookahead.

Opção 2 regex (?sx)--\nare.*?(?=\n--|\Z)\n

(?sx)  # match the remainder of the pattern with the following eff. flags: sx
       #        s modifier: single line. Dot matches newline characters
       #        x modifier: extended. Spaces and text after a # in 
       #        the pattern are ignored
--     # matches the characters -- literally (case sensitive)
\n     # matches a line-feed (newline) character (ASCII 10)
are    # matches the characters are literally (case sensitive)
.*?    # matches any character 
       #        Quantifier — Matches between zero and unlimited times,
       #        as few times as possible, expanding as needed (lazy).
(?=    # Positive Lookahead (?=\n--|\Z)
       # Assert that the Regex below matches
       #        1st Alternative \n--
\n     #               matches a line-feed (newline) character (ASCII 10)
--     #               matches the characters -- literally.
|      #        2nd Alternative \Z
\Z     #               \Z asserts position at the end of the string, or
       #               before the line terminator right at
       #               the end of the string (if any)
)      # Close the lookahead parenthesis.
\n     #        matches a line-feed (newline) character (ASCII 10)

Opção 3 regex (?xs)--\nare(?:(?!\n--).)*\n

(?xs)  # match the remainder of the pattern with the following eff. flags: xs
       # modifier x : extended. Spaces and text after a # in are ignored
       # modifier s : single line. Dot matches newline characters
--     # matches the characters -- literally (case sensitive)
\n     # matches a line-feed (newline) character (ASCII 10)
are    # matches the characters are literally (case sensitive)
(?:    # Non-capturing group (?:(?!\n--).)
(?!    #      Negative Lookahead (?!\n--)
       #           Assert that the Regex below does not match
\n     #                matches a line-feed (newline) character (ASCII 10)
--     #                matches the characters -- literally
)      #      Close Negative lookahead
.      #      matches any character
)      # Close the Non-Capturing group.
*      # Quantifier — Matches between zero and unlimited times, as many
       # times as possible, giving back as needed (greedy)
\n     # matches a line-feed (newline) character (ASCII 10)

sed

$ sed -nEe 'bend
            :start  ;N;/^--\nare/!b
            :loop   ;/^--$/!{p;n;bloop}
            :end    ;/^--$/bstart'           infile
    
por 21.07.2018 / 07:27
1

Usando o GNU awk ou mawk :

$ awk -v word="are" -v RS='--\n' -v ORS='--\n' '$1 ~ "^" word "[[:punct:]]?"' file
are you happy
--
are(you hungry
too
--

Isso define o separador de registro para entrada e saída para -- seguido por uma nova linha. A primeira palavra de cada parágrafo é encontrada em $1 . Nós combinamos isso com a palavra dada (potencialmente seguida por um caracter de pontuação). Se eles corresponderem, o parágrafo será impresso.

Observe que os marcadores de parágrafo na saída serão colocados no final de cada parágrafo e não no início, já que usamos ORS para exibi-los.

Usando um script sed :

:top
/^--/!d;                   # This is not a new paragraph, delete
N;                         # Append next line
/^--\nare[[:punct:]]?/!d;  # This is not a paragraph we want, delete
:record
n;                         # Output line, get next
/^--/!brecord;             # Not yet done with this record, branch to :record
btop;                      # Branch to :top

Em exibição:

$ sed -E -f script.sed file
--
are you happy
--
are(you hungry
too

Ou como uma linha única que usa a variável de shell $word :

sed -E -e ':t;/^--/!d;N;' \
       -e "/^--\n$word[[:punct:]]?/!d" \
       -e ':r;n;/^--/!br;bt' file
    
por 20.07.2018 / 18:04
0
perl -l -0777ne 'print $& while /^--\nare\b.*?(?=\n(?:--|\z))/msg' inp.file

perl -F'/^--\n/m' -na0777e 'print "--\n$_" for  grep { /^are\b/ } @F' inp.file

sed -e '
 /--/!{H;$!d;s/.*/--/;}
 1d;x;s/.//
 /^are\>/!{s/.*//;h;d;}
 H;s/.*//;x
' inp.file

Voltarei mais tarde para explicações de trabalho.

    
por 20.07.2018 / 22:39
0

Olhando para a sua pergunta, senti que deveria ser possível resolvê-la usando grep + PCREs .

  • O método # 1 resolve isso, graças a @issac pela ajuda em martelá-lo.
  • O método # 2 mostra como você pode usar o modificador in-line ( (?s) ) e lookahead ( ?!... ).
  • Minha solução original (# 3) funciona bem na maioria das situações, exceto pelos tipos que eu destaque abaixo em sua seção.

método grep # 1

$ grep -Pzo -- '--\nare([^\n]*\n)+?(?=--|\Z)' afile

Como funciona

grep muda
  • -P - extensões PCRE ativadas
  • -z - trata entrada como multi-linha, usando NUL no lugar de \n (newlines)
  • -o - mostra apenas correspondências
regex
  • %código%
    • corresponde aos traços duplos seguidos por um --\nare([^\n]*\n)+?(?=--|\Z) e, em seguida, a uma continuação de zero ou mais caracteres não pertencentes à nova linha - ou - uma nova linha.
    • O are corresponderá a 1 ou mais, mas não é ganancioso, portanto, não continuará de forma agressiva.
    • Finalmente, os +? guardam para o final do bloco procurando o próximo traço duplo (?=--|\Z) ou o final do arquivo ( -- ).

método grep # 2

Este método usa o modificador inline DOTALL para obter \Z para corresponder a novas linhas ('n').

$ grep -Pzo -- '(?s)--\nare((?!\n--).)+\n' afile

Como funciona

grep muda
  • . - extensões PCRE ativadas
  • -P - trata entrada como multi-linha, usando NUL no lugar de -z (newlines)
  • \n - mostra apenas correspondências
regex
  • -o - modificador inline DOTALL - alls ponto para corresponder à nova linha
  • (?s) - corresponde a uma nova linha, seguida de --\nare
  • are - corresponde a caracteres ((?!\n--).)+\n desde que a antevisão . não encontre (?!\n--) . Esse bloco inteiro de correspondência precisa ser pelo menos um ou mais ( \n-- ) e seguido por uma nova linha, + .

método grep # 3 (original)

Aqui está uma solução de \n que utiliza a extensão PCRE ( grep ). Este método funcionou para todos os exemplos fornecidos, ele falharia com exemplos como este:

--
are
some-other-dasher

Mas, na maioria dos casos, na maioria dos casos, posso conceber ter que lidar com isso.

$ grep -Pzo -- '--\nare[^\r\n]+[^-]+' afile
--
are you happy

--
are(you hungry
too

Como funciona

grep muda
  • -P - extensões PCRE ativadas
  • -P - trata entrada como multi-linha, usando NUL no lugar de -z (newlines)
  • \n - mostra apenas correspondências
regex
  • %código%
    • corresponde aos traços duplos seguidos por uma nova linha e a palavra -o .
    • Ele continuará imprimindo o restante da linha em que '--\nare[^\r\n]+[^-]+' está ativo até que uma nova linha seja encontrada.
    • Em seguida, ele imprime caracteres até que uma sequência de traços seja encontrada.

Referências

por 21.07.2018 / 05:08