Изменить порядок строк в файле

Изменить порядок строк в файле

Я пытаюсь изменить порядок строк в определенном шаблоне. Работаю с файлом со многими строками (например, 99 строк). Для каждых трех строк я хотел бы, чтобы вторая строка была третьей, а третья — второй.

ПРИМЕР.

1- Ввод:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2- Выход:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...

решение1

$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

То есть, pнапечатать текущую строку, получить дополнительную n, hобновить ее, получить nдополнительную, Gсохранить сохраненную строку (добавить ее к пространству шаблона) и pнапечатать это пространство шаблона из 2 строк, поменяв местами третью и вторую строки.

решение2

Использование awkцелочисленной математики:

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

Оператор деления по модулю выполняет целочисленное деление и возвращает остаток, поэтому для каждой строки он вернет последовательность 1, 2, 0, 1, 2, 0 [...]. Зная это, мы просто сохраняем ввод в строках, где модуль равен 2, на будущее — то есть сразу после печати ввода, когда он равен нулю.

решение3

Использование perlи короткий скрипт:

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.

Скрипт обрабатывает весь файл, для каждой строки (хранящейся в $_) он получает следующие две строки ( $l2и $l3) и выводит их в требуемом порядке: строка1, строка3, строка2.

решение4

Перл

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

Идея здесь в том, что мы используем оператор остатка от деления %с $.переменной номера строки, чтобы выяснить, какая из них является каждой первой, какая — каждой второй, а какая — каждой третьей строкой. Для каждой третьей строки остаток равен 0, тогда как для каждой первой и второй строки он будет иметь соответствующие числа.

Тест:

$ 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.

Незначительное улучшение

Подход с сохранением второй строки в переменной имеет изъян. Что, если последняя строка является "второй", т. е. для этой строки остаток равен 2? Исходный код в моем ответе и ответе DopeGhoti не будет напечатан, My dog is orangeесли мы опустим последнюю строку. Исправление этого в обоих случаях — использовать END{}блок кода с отменой временной переменной после печати. ​​Другими словами:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

и

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

Таким образом, код будет работать для произвольного количества строк в файле, а не только для тех, которые делятся на 3.

Дополнительное исправление проблемы, упомянутой в комментариях

В случае awk, если последняя строка в файле выводит 1 для $. % 3, предыдущий код имеет проблему вывода пустой новой строки из-за безусловной печати END{print delay}, поскольку printфункция, упомянутая в комментариях, всегда добавляет новую строку к любой переменной, с которой она работает. В случае perlверсии эта проблема не возникает, поскольку функция с -neфлагами printне добавляет новую строку.

Тем не менее, исправление в случае awk заключается в том, чтобы сделать условие, как упоминал Dope Ghoti в комментариях, для проверки длины временной переменной. Perl-версия того же исправления будет:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 

Связанный контент