
Estoy intentando cambiar el orden de las líneas en un patrón específico. Trabajar con un archivo con muchas líneas (ej. 99 líneas). Por cada tres líneas, me gustaría que la segunda línea fuera la tercera y la tercera la segunda.
EJEMPLO.
1- Entrada:
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...
2- Salida:
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...
Respuesta1
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8
Es decir, p
imprima la línea actual, obtenga la n
ext, h
la anterior, obtenga la n
ext, G
et la línea retenida (añádala al espacio del patrón) e p
imprima ese espacio del patrón de 2 líneas con la tercera y la segunda línea intercambiadas.
Respuesta2
Usando awk
y matemáticas enteras:
awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input
El operador de módulo realiza la división de enteros y devuelve el resto, por lo que para cada línea devolverá la secuencia 1, 2, 0, 1, 2, 0 [...]. Sabiendo eso, simplemente guardamos la entrada en las líneas donde el módulo es 2 para más adelante, es decir, justo después de imprimir la entrada cuando es cero.
Respuesta3
Usando perl
y un guión corto:
user@pc:~$ cat input.txt
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
El script procesa todo el archivo, para cada línea (almacenada en $_
) obtendrá las dos líneas siguientes ( $l2
y $l3
) y las imprimirá en el orden solicitado: línea1, línea3, línea2.
Respuesta4
perla
perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt
La idea aquí es que usemos el operador de módulo %
con $.
la variable de número de línea, para determinar cuál es cada primera, cuál es cada segunda y cuál es cada tercera línea. Para cada tercera línea el resto es 0, mientras que para cada primera y segunda línea tendrá los números correspondientes.
Prueba:
$ cat input.txt
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
Mejora menor
El enfoque de almacenar la segunda línea en una variable tiene un defecto. ¿Qué pasa si la última línea es la "segunda", es decir, para ese número de línea el resto es 2? El código original en mi respuesta y la de DopeGhoti no se imprimirá My dog is orange
si omitimos la última línea. La solución para esto en ambos casos es usar END{}
un bloque de código y desarmar la variable temporal después de la impresión. En otras palabras:
$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt
y
$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt
De esta manera, el código funcionará para un número arbitrario de líneas en un archivo, no solo para aquellas divisibles por 3.
Solución adicional para el problema mencionado en los comentarios.
En el caso de awk, si la última línea del archivo produce un resultado de 1 para $. % 3, el código anterior tiene el problema de generar una nueva línea en blanco debido a la impresión incondicional de END{print delay}
, ya que print
la función mencionada en los comentarios siempre agrega una nueva línea a cualquier variable en la que esté operando. En el caso de perl
la versión, este problema no ocurre, ya que la función with -ne
flags print
no agrega la nueva línea.
No obstante, la solución en el caso de awk es hacer condicional, como lo menciona Dope Ghoti en los comentarios, es verificar la longitud de la variable temporal. La versión Perl de la misma solución sería:
$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt