¿Cómo fusionar cada dos líneas en una desde la línea de comando?

Tengo un archivo de texto con el siguiente formato. La primera línea es la “CLAVE” y la segunda línea es el “VALOR”.

KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

Necesito el valor en la misma línea que la clave. Entonces la salida debería verse así …

 KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

Sería mejor si pudiera usar algún delimitador como $ o,:

 KEY 4048:1736 string , 3 

¿Cómo combino dos líneas en una?

awk:

 awk 'NR%2{printf "%s ",$0;next;}1' yourFile 

nota, hay una línea vacía al final de la salida.

sed:

 sed 'N;s/\n/ /' yourFile 

paste es bueno para este trabajo:

 paste -d " " - - < filename 

Hay más formas de matar a un perro que colgando. [1]

 awk '{key=$0; getline; print key ", " $0;}' 

Pon el delimitador que quieras dentro de las comillas.


Referencias

  1. Originalmente “Muchas formas de desollar al gato”, revertido a una expresión más antigua y potencialmente originaria que tampoco tiene nada que ver con las mascotas.

Alternativa a sed, awk, grep:

 xargs -n2 -d'\n' 

Esto es mejor cuando desea unir N líneas y solo necesita una salida delimitada por espacios.

Mi respuesta original fue xargs -n2 que se separa en palabras en lugar de líneas. -d se puede usar para dividir la entrada por cualquier carácter individual.

Aquí está mi solución en bash:

 while read line1; do read line2; echo "$line1, $line2"; done < data.txt 

Aunque parece que las soluciones anteriores funcionarían, si se produce una sola anomalía en el documento, la salida se haría pedazos. Debajo está un poco más seguro.

 sed -n '/KEY/{ N s/\n/ /p }' somefile.txt 

Aquí hay otra manera con awk :

 awk 'ORS=NR%2?FS:RS' file 

 $ cat file KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

 $ awk 'ORS=NR%2?FS:RS' file KEY 4048:1736 string 3 KEY 0:1772 string 1 KEY 4192:1349 string 1 KEY 7329:2407 string 2 KEY 0:1774 string 1 

Según lo indicado por Ed Morton en los comentarios, es mejor agregar llaves para seguridad y parens para portabilidad.

 awk '{ ORS = (NR%2 ? FS : RS) } 1' file 

ORS significa Output Record Separator. Lo que estamos haciendo aquí es probar una condición usando el NR que almacena el número de línea. Si el módulo de NR es un valor verdadero (> 0) entonces establecemos el Separador de Campo de Salida al valor de FS (Separador de Campo) que por defecto es espacio, de lo contrario asignamos el valor de RS (Separador de Registro) que es nueva línea.

Si desea agregar , como separador, use lo siguiente:

 awk '{ ORS = (NR%2 ? "," : RS) } 1' file 

“ex” es un editor de líneas progtwigble que pertenece a la misma familia que sed, awk, grep, etc. Creo que podría ser lo que estás buscando. Muchos vi-clones / sucesores modernos también tienen un modo vi.

  ex -c "%g/KEY/j" -c "wq" data.txt 

Esto dice que para cada línea, si coincide con “KEY”, realice un j oin de la siguiente línea. Después de que el comando se complete (contra todas las líneas), emita un mensaje y una orden.

Si Perl es una opción, puede intentar:

 perl -0pe 's/(.*)\n(.*)\n/$1 $2\n/g' file.txt 

Puedes usar awk como este para combinar 2 pares de líneas:

 awk '{ if (NR%2 != 0) line=$0; else {printf("%s %s\n", line, $0); line="";} } \ END {if (length(line)) print line;}' flle 

También puede usar el siguiente comando vi:

 :%g/.*/j 
 nawk '$0 ~ /string$/ {printf "%s ",$0; getline; printf "%s\n", $0}' filename 

Esto se lee como

 $0 ~ /string$/ ## matches any lines that end with the word string printf ## so print the first line without newline getline ## get the next line printf "%s\n" ## print the whole line and carriage return 

En el caso en que necesitaba combinar dos líneas (para un procesamiento más fácil), pero permitía que los datos pasasen el específico, encontré que esto era útil

data.txt

 string1=x string2=y string3 string4 

cat data.txt | nawk ‘$ 0 ~ / string1 = / {printf “% s”, $ 0; obtener línea; printf “% s \ n”, $ 0; getline} {print} ‘> converted_data.txt

La salida se ve así:

convert_data.txt

 string1=x string2=y string3 string4 

Otra solución que usa vim (solo como referencia).

Solución 1 :

Abra el archivo en el vim filename archivo vim vim filename , luego ejecute el comando :% normal Jj

Este comando es fácil de entender:

  • %: para todas las líneas,
  • normal: ejecuta el comando normal
  • Jj: ejecuta el comando Unir, luego salta a la línea inferior

Después de eso, guarde el archivo y salga con :wq

Solución 2 :

Ejecute el comando en shell, vim -c ":% normal Jj" filename , luego guarde el archivo y salga con :wq .

Una ligera variación en la respuesta de glenn jackman usando paste : si el valor para la opción de delimitador -d contiene más de un carácter, paste cíclicamente los caracteres uno por uno, y combinado con las opciones -s sigue haciéndolo mientras procesa el mismo archivo de entrada .

Esto significa que podemos usar lo que queramos tener como separador más la secuencia de escape \n para unir dos líneas a la vez.

Usando una coma:

 $ paste -s -d ',\n' infile KEY 4048:1736 string,3 KEY 0:1772 string,1 KEY 4192:1349 string,1 KEY 7329:2407 string,2 KEY 0:1774 string,1 

y el signo de dólar:

 $ paste -s -d '$\n' infile KEY 4048:1736 string$3 KEY 0:1772 string$1 KEY 4192:1349 string$1 KEY 7329:2407 string$2 KEY 0:1774 string$1 

Lo que no se puede hacer es usar un separador que consta de varios caracteres.

Como extra, si la paste cumple con POSIX, esto no modificará la nueva línea de la última línea del archivo, por lo que para un archivo de entrada con un número impar de líneas como

 KEY 4048:1736 string 3 KEY 0:1772 string 

paste no paste en el carácter de separación en la última línea:

 $ paste -s -d ',\n' infile KEY 4048:1736 string,3 KEY 0:1772 string 

La forma más simple está aquí:

  1. Elimine las líneas pares y escríbalas en algún archivo temporal 1.
  2. Elimine las líneas impares y escríbalas en algún archivo temporal 2.
  3. Combina dos archivos en uno usando el comando pegar con -d (significa eliminar espacio)

 sed '0~2d' file > 1 && sed '1~2d' file > 2 && paste -d " " 1 2 

 perl -0pE 's{^KEY.*?\K\s+(\d+)$}{ $1}msg;' data.txt > data_merged-lines.txt 

-0 engulle todo el archivo en lugar de leerlo línea por línea;
pE ajusta el código con loop e imprime el resultado, consulte los detalles en http://perldoc.perl.org/perlrun.html ;
^KEY coincida con “KEY” en el comienzo de la línea, seguido de una coincidencia no codiciosa de cualquier cosa ( .*? ) Antes de la secuencia de

  1. uno o más espacios \s+ de cualquier tipo, incluidos los saltos de línea;
  2. uno o más dígitos (\d+) que capturamos y luego reinsertamos como $1 ;

seguido por el final de la línea $ .

\K convenientemente excluye todo lo que está a su izquierda de la sustitución, por lo que { $1} reemplaza solo una o dos secuencias, consulte http://perldoc.perl.org/perlre.html .

Una solución más general (permite unir más de una línea de seguimiento) como un script de shell. Esto agrega una línea entre cada uno, porque necesitaba visibilidad, pero eso se remedia fácilmente. Este ejemplo es donde la línea “clave” terminó en: y ninguna otra línea lo hizo.

 #!/bin/bash # # join "The rest of the story" when the first line of each story # matches $PATTERN # Nice for looking for specific changes in bart output # PATTERN='*:'; LINEOUT="" while read line; do case $line in $PATTERN) echo "" echo $LINEOUT LINEOUT="$line" ;; "") LINEOUT="" echo "" ;; *) LINEOUT="$LINEOUT $line" ;; esac done 

Pruebe la siguiente línea:

 while read line1; do read line2; echo "$line1 $line2"; done new_file 

Poner delimitador en el medio

 "$line1 $line2"; 

por ejemplo, si el delimitador es | , entonces:

 "$line1|$line2"; 

Puedes usar xargs así:

 xargs -a file