Como posso juntar duas linhas em um arquivo se ambas se enquadram em um padrão?

Como posso juntar duas linhas em um arquivo se ambas se enquadram em um padrão?

Eu tenho um arquivo com várias linhas e quero juntar as linhas se ambas se enquadrarem em um padrão específico.

Eu sei que posso encontrar linhas que se encaixem no padrão e obter a próxima linha com:

grep -E -A1 'Pattern' filename

Mas como posso verificar se a próxima linha também se enquadra no padrão e como faria para unir as duas?

Por exemplo, tenho um arquivo como este:

Hello
i
am
John
Smith

Um padrão de exemplo poderia ser o seguinte:

'^[A-Z][a-z]+'

Então, neste caso, eu gostaria de combinar as linhas, se ambas começarem com letras maiúsculas.

A saída que eu gostaria de alcançar seria:

Hello
i
am 
John Smith

Responder1

/^[A-Z][a-z]+/{
  :a
  N
  /\n[A-Z][a-z]+/{
    s/\n/ /
    b a
  }
}

Salve como join.sede para executar: sed -Ef join.sed file.

Se a linha corresponder ao padrão, iniciamos um loop que anexa a próxima linha ao espaço do padrão e substitui o caractere de nova linha por um espaço, desde que essa linha também corresponda ao padrão.

Para GNU Sed você pode reduzi-lo para uma linha:

sed -E '/^[A-Z][a-z]+/{:a;N;/\n[A-Z][a-z]+/{s/\n/ /;b a}}' file

Alternativamente, um script Awk join.awk, para o qual o padrão deve ser fornecido como p:

{
    if($0~p)c+=1
    else c=0
    printf "%s%s", (c>1 ? " " : ors), $0
    ors=ORS
}
END{print ""}

Executar: awk -f join.awk p='^[A-Z][a-z]+' file.

Responder2

Usando sedcaractere nulo como separador ( -z):

$ sed -z 's/\([A-Z][a-z]\+\)\n\([A-Z][a-z]\+\)/\1 \2/'
Hello
i
am
John Smith

Responder3

Usando Raku (anteriormente conhecido como Perl_6)

raku -e 'given lines.join("\n") { S/ $<first>=[<upper><lower>+] \n $<last>=[<upper><lower>+] /$<first> $<last>/.put};'

Entrada de amostra:

Hello
i
am
John
Smith
goodbye

Saída de amostra:

Hello
i
am
John Smith
goodbye

Acima está uma solução codificada em Raku, um membro da família de linguagens Perl. Os dados são givenpara Raku na forma de lines, mas como linesa entrada automática de rotina do Raku, os dados são joineditados com novas linhas. Embora isso possa parecer um pouco complicado, a vantagem é que a linesrotina do Raku lê os dados preguiçosamente, ou seja, o código acimadeveria estareficiente em termos de memória.

Raku implementa um S///operador "não destrutivo", que é semelhante (se não idêntico) ao s///operador familiar (Raku também tem esse). O Soperador de capital tem uma vantagem na medida em que"deixa a string original intacta e retorna a string resultante em vez de $/ (a variável de correspondência)."

Dentro da metade correspondente (esquerda) do S///operador,capturas nomeadasestão empregados. O mecanismo regex primeiro procura [<upper><lower>+]e atribui-o ao capture nomeado $<first>, depois procura por a \n(nova linha) e, finalmente, procura outro [<upper><lower>+], desta vez atribuindo-o ao capture nomeado $<last>. Para finalizar, dentro da metade de substituição (direita) do S///operador, as duas capturas nomeadas $<first> $<last>são usadas para substituir a correspondência do lado esquerdo, emboracomum espaço esema \nnova linha no meio.

Uma maneira alternativa de fazer a mesma coisa está abaixo. O código omite capturas nomeadas, em vez disso, usa <(\n)>para descartar tudo do objeto de correspondência, exceto o que está dentro dos <(…)>marcadores de captura. Então, na substituição, \n é substituído por espaço:

raku -e 'put S/ [<upper><lower>+] <(\n)> [<upper><lower>+] / / given lines.join("\n");'  

[Observe que o código acima só será reduzido George\nHerbert\nWalker\nBushde 4 linhas para 3 ( George Herbert\nWalker\nBush). Se você quiser que todas as ocorrências consecutivas em linha sejam [<upper><lower>+]retornadas em uma linha, sinta-se à vontade para postar essa pergunta].

https://docs.raku.org/linguagem/regexes#S///_non-destructive_substitution
https://docs.raku.org/language/regexes#index-entry-regex__Named_captures-Named_captures
https://raku.org

informação relacionada