¿Cómo puedo unir dos líneas en un archivo si ambas se ajustan a un patrón?

¿Cómo puedo unir dos líneas en un archivo si ambas se ajustan a un patrón?

Tengo un archivo con varias líneas y quiero unir líneas si ambas se ajustan a un patrón específico.

Sé que puedo encontrar líneas que se ajusten al patrón y obtener la siguiente línea con:

grep -E -A1 'Pattern' filename

Pero, ¿cómo puedo comprobar si la siguiente línea también se ajusta al patrón y cómo hago para unir las dos?

Por ejemplo, tengo un archivo como este:

Hello
i
am
John
Smith

Un patrón de ejemplo podría ser el siguiente:

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

Entonces, en este caso, me gustaría combinar las filas, si ambas comienzan con letras mayúsculas.

El resultado que me gustaría lograr sería:

Hello
i
am 
John Smith

Respuesta1

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

Guárdelo como join.sedy para ejecutar: sed -Ef join.sed file.

Si la línea coincide con el patrón, iniciamos un bucle que agrega la siguiente línea al espacio del patrón y reemplaza el carácter de nueva línea con un espacio siempre que esa línea también coincida con el patrón.

Para GNU Sed, puedes contraerlo en una sola línea:

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

Alternativamente, un script Awk join.awk, para el cual el patrón debe proporcionarse como p:

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

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

Respuesta2

Utilizando sedcarácter nulo como separador ( -z):

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

Respuesta3

Usando Raku (anteriormente conocido como Perl_6)

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

Entrada de muestra:

Hello
i
am
John
Smith
goodbye

Salida de muestra:

Hello
i
am
John Smith
goodbye

Arriba hay una solución codificada en Raku, un miembro de la familia de lenguajes Perl. Los datos se envían givena Raku en forma de , pero como la rutina de entrada automática linesde Raku , los datos se completan con nuevas líneas. Si bien esto puede parecer un poco complicado, la ventaja es que la rutina de Raku lee los datos con pereza, es decir, el código anterior.linesjoinlinesdebiera sermemoria eficiente.

Raku implementa un S///operador "no destructivo", que es similar (si no idéntico) al s///operador familiar (Raku también tiene ese). El Soperador de capital tiene una ventaja:"deja la cadena original intacta y devuelve la cadena resultante en lugar de $/ (la variable de coincidencia)".

Dentro de la mitad coincidente (izquierda) del S///operador,capturas nombradasestan empleados. El motor de expresiones regulares primero lo busca [<upper><lower>+]y lo asigna a la captura con nombre $<first>, luego busca una \n(nueva línea) y finalmente busca otra [<upper><lower>+], esta vez asignándola a la captura con nombre $<last>. Para terminar, dentro de la mitad de sustitución (derecha) del S///operador, las dos capturas nombradas $<first> $<last>se utilizan para reemplazar la coincidencia del lado izquierdo, aunqueconun espacio ysinla \nnueva línea en el medio.

A continuación se muestra una forma alternativa de lograr lo mismo. El código omite las capturas con nombre y, en su lugar, las utiliza <(\n)>para eliminar todo del objeto coincidente, excepto lo que está dentro de los <(…)>marcadores de captura. Luego, en el reemplazo, \n se reemplaza con espacio:

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

[Tenga en cuenta que el código anterior solo colapsará algo así como George\nHerbert\nWalker\nBushde 4 líneas a 3 ( George Herbert\nWalker\nBush). Si desea que todas las apariciones consecutivas de líneas [<upper><lower>+]se devuelvan en una sola línea, no dude en publicar esa pregunta].

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

información relacionada