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.sed
y 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 sed
cará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 given
a Raku en forma de , pero como la rutina de entrada automática lines
de 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.lines
join
lines
debiera 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 S
operador 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 \n
nueva 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\nBush
de 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