Eliminar el carácter de nueva línea solo cada N líneas


16

Procesando texto, necesito eliminar el carácter de nueva línea cada dos líneas.

Texto de ejemplo:

this is line one
and this is line two
the third and the
fourth must be pasted too

Salida deseada:

this is line one and this is line two
the third and the fourth must be pasted too

Intenté un whilebucle, pero un bucle while es una mala práctica. ¿Es posible hacerlo usando tro cualquier otro comando?


44
El título dice "cada N líneas", pero en la pregunta y el ejemplo es "cada 2 líneas". La mayoría de las respuestas funcionan solo para N = 2. ¿Estás buscando algo que funcione para todos los N?
JigglyNaga

Esa es la clave. Todos respondieron por 2 líneas, pero necesitaría usar N = 3 o N = 4
jomaweb

Respuestas:


24

paste(también una utilidad simple estándar POSIX como tr) es su herramienta para eso.

Suponiendo que desea que esos caracteres de nueva línea se reemplacen con un espacio en lugar de simplemente eliminarse como en su muestra:

paste -d ' ' - - < file

O:

paste -sd ' \n' file

Reemplace ' 'con '\0'si realmente desea que se eliminen.

Para reemplazar 2 de 3:

paste -sd '  \n' file

1 de 3, comenzando con el segundo:

paste -sd '\n \n' file

Y así.

Otra cosa buena pastees que no dejará una línea sin terminar. Por ejemplo, si elimina cada nueva línea en un archivo (como con tr -d '\n' < fileo tr '\n' ' ' < file), termina sin ninguna línea ya que las líneas deben terminarse con un carácter de nueva línea. Por lo tanto, generalmente es mejor usarlo pastepara eso (como en paste -sd '\0' fileo paste -sd ' ' file) que agregará ese carácter de nueva línea final necesario para tener texto válido.


11

Con sed moderno GNU

sed -rz 's/\n([^\n]*\n)/ \1/g' sample.text

Y awk

awk '{getline line2;print $0, line2}' sample.text

3
Ese sedenfoque significa sorber todo el archivo en la memoria (siempre que no contenga bytes NUL) y realizar una costosa sustitución de expresiones regulares. No puedo ver el beneficio sobre el sed 'N;s/\n/ /'enfoque estándar .
Stéphane Chazelas

6

Use sedpara esto como se muestra a continuación:

SHW@SHW:/tmp $ cat a
this is line one
and this is line two
the third and the
fourth must be pasted too

SHW@SHW:/tmp $ sed 'N;s/\n/ /' a -i

SHW@SHW:/tmp $ cat a
this is line one and this is line two
the third and the fourth must be pasted too

4

Otra forma es usar xargs:

$ < txt xargs -d '\n' -n 2 echo
this is line one and this is line two
the third and the fourth must be pasted too

dónde

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Aunque, esta solución es bastante excesiva porque echose ejecuta un proceso para cada línea ... Por lo tanto, además de los ejemplos de juguetes, se debe preferir una solución basada en awk / sed o similar.


1
Dependiendo de su echoimplementación, también tendrá problemas con los caracteres de barra invertida o algunas líneas que comienzan con -(como --helpo -nenecon GNU echo). También tenga en cuenta que -des una extensión GNU.
Stéphane Chazelas

Para evitar problemas con echo, puede usar esto:< txt xargs -d '\n' -n 2 printf -- '%s %s\n'
nyuszika7h

4

Esto es realmente extremadamente simple en vim. Para unir cada línea, use el Jcomando, luego use el %normcomando para aplicarlo a cada línea simultáneamente. Por ejemplo

:%norm J<CR>

(Solo en caso de que no estés familiarizado con vim, <CR>solo significa entrar)

Esto incluso funciona para unir un número arbitrario de líneas. Por ejemplo, unir cada diez líneas sería

:%norm 9J<CR>

Si no se siente cómodo con vim y prefiere usarlo como una herramienta de línea de comandos, en lugar de un editor de texto interactivo, puede hacer lo siguiente:

vim myfile -c '%norm J' -c 'wq'

¿Le gustaría al votante explicar qué puedo hacer para mejorar esta respuesta?
DJMcMayhem

3
$ awk '{printf "%s%s",$0,(NR%2?" ":"\n")}' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Esto imprime cada línea, $0seguida de un espacio o una nueva línea, dependiendo de si el número de línea NRes impar o par.

La expresión NR%2?" ":"\n"es una declaración ternaria. La expresión se NR%2evalúa como verdadera (distinta de cero) si el número de fila es impar. En el caso, la expresión ternaria devuelve un espacio. Si se evalúa como falso (cero), se devuelve la nueva línea.

Alternativa

Como lo sugirió Costas en los comentarios:

$ awk '{ORS=(NR%2?" ":RS)}1' sample.txt
this is line one and this is line two
the third and the fourth must be pasted too

Aquí, la declaración ternaria NR%2?" ":RSse usa para devolver un espacio o el separador de registro de entrada ( RS, predeterminado = nueva línea). Este valor se asigna al separador de registro de salida, ORS. El 1final del comando es la abreviatura críptica de awk para imprimir el registro.


Todavía puede guardar 3 caracteres: los ()paréntesis y el espacio después printf;)
maxschlepzig

1
¿Ternario? Oh! 'NR%2{printf("%s ",$0);next}1'
Costas

Con la respuesta de maxschlepzig y la declaración ternaria:'{ORS=(NR%2?" ":RS)}1'
Costas

@Costas me gusta eso. Respuesta actualizada con ORSsolución.
John1024

2

Solución genérica, reemplazar 5con el número de líneas requeridas

$ # eof to ensure last line has newline ending
$ seq 16 | perl -pe 's/\n/ / if ++$i%5 && !eof'
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

$ # or just use pr
$ seq 16 | pr -5ats' '
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16

1

Puedes usar awkpara esto:

$ awk '{c="\n"} NR%2 {c=" "} { printf("%s%s", $0, c) } ' txt

Produce:

this is line one and this is line two
the third and the fourth must be pasted too

dónde:

$ cat txt
this is line one
and this is line two
the third and the
fourth must be pasted too

Las awkacciones se ejecutan para cada línea, la variable especial hace $0referencia a la línea actual, NRes el número de línea actual (comenzando en 1). La segunda acción está protegida por la expresión NR%2, que es la operación de módulo. Por lo tanto, c=" "solo se ejecuta si NR%2es verdadero, es decir, para números de línea impares.

La awksintaxis es similar a C, pero algunos elementos son opcionales en algunos contextos, por ejemplo, punto y coma.


Su cvariable es ORS:'NR%2{ORS=" "}1;{ORS=RS}'
Costas

0

Utilizando ed:

$ cat text
this is line one
and this is line two
the third and the
fourth must be pasted too
this is line one
and this is line two
the third and the
fourth must be pasted too

$ ed text <<'END_ED'
g/./s/$/ /\
j
w text.new
END_ED
164
164

$ cat text.new
this is line one and this is line two
the third and the fourth must be pasted too
this is line one and this is line two
the third and the fourth must be pasted too

Los edcomandos de edición, para cada línea ( gaplica un conjunto de comandos de edición a cada línea que coincida con la expresión regular dada), agregarán un carácter de espacio al final y lo unirán a la siguiente línea. Luego escribe el texto resultante en un archivo llamado text.new.


0

Con Ruby

Supongo que cada bloque de nlíneas se unirán. Supongamos que n = 3el archivo de entrada es 'infile'y los resultados deben escribirse en el archivo 'outfile'.

Construir un archivo

Ruby -e "File.write 'infile', <<_
> Line 1
> Line 2
> Line 3
> Line 4
> Line 5
> Line 6
> Line 7
> _"

Confirme el contenido del archivo.

ruby -e "p File.read 'infile'"
  # "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\n"

Eliminar nuevas líneas y escribir en el archivo

ruby -e "File.write 'outfile', File.readlines('infile').
  each_with_index { |line,i| line.chomp! unless (i+1)%3==0 }"

Confirmar contenidos

ruby -e "puts File.read 'outfile'"
  # ["Line 1", "Line 2", "Line 3\n", "Line 4", "Line 5", "Line 6\n", "Line 7"]

1
Bueno uno En teoría, rubyestá fuera de tema en U&L. Pero, dado que lo está utilizando desde la línea de comandos con ruby -e, eso lo convierte en un tema suficiente.
grochmal
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.