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

11

Я пытаюсь изменить порядок линий в определенном порядке. Работа с файлом с несколькими строками (например, 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.
...
Анник Рэймонд
источник

Ответы:

12

Использование 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 на более поздний срок, то есть сразу после печати ввода, когда он равен нулю.

DopeGhoti
источник
У нас здесь небольшой недостаток. Смотрите мой ответ, небольшая часть улучшения
Сергей Колодяжный
Спасибо за хороший улов; Я включил исправление в свой ответ в форме NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

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

Стефан Шазелас
источник
3

Другой подход к awk :

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

Выход:

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

  • (getline L2)>0 && (getline L3)>0- извлекает следующие 2 записи, если они существуют

  • каждая 2-я и 3-я записи назначаются L2и L3переменным соответственно

RomanPerekhrest
источник
1
Я предполагаю, что эти переменные начинаются с буквы L (нижний регистр). Это плохой выбор для читабельности, потому что они выглядят как цифры для двенадцати и тринадцати. Лучший выбор может быть line2и т. Д.
Приостановлено до дальнейшего уведомления.
@DennisWilliamson, изменен на верхний регистр
RomanPerekhrest
1

Использование 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) и печатает их в требуемом порядке: line1, line3, line2.

Фрэнк Фёрстер
источник
1

Один из способов может быть следующим:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)/\3\2\1/;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

С другой стороны,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

Результаты

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

источник
1

Почему бы просто не сделать цикл времени? В развернутом виде:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

В «однострочном формате»:

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

Выходы:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
Стивен Куан
источник
1

Perl

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

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

Тестовое задание:

$ 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? Исходный код в ответах my и 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версии эта проблема не возникает, так как функция -neflags 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 
Сергей Колодяжный
источник
1
У вашего исправления есть потенциальный незначительный недостаток в том, что он добавит пустую строку вывода для файлов с «неправильным» числом строк. Я исправил это, включив ваше улучшение в мой ответ с (для awk) NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
1
@DopeGhoti Проблема не возникает с Perl, так как печать Perl с -neфлагами не выводит символ новой строки. Он действительно печатает, но это пустая строка, без завершающей строки. Тем не менее, я добавил упоминание о проблеме и то же исправление в свой ответ. Благодарность !
Сергей Колодяжный
1

напор

Не подходит для длинных файлов, но все же удобно, если вы просто редактировали файл и хотели, например, изменить порядок некоторых разделов yaml.

Сначала запишите макрос:

gg qq j ddp j q

И затем повторите желаемое количество раз:

@q @q @q ...

Или просто например

3@q

Объяснение:

  • gg - перейти на первую строку
  • qq - начать запись макроса
  • j - перейти на вторую строку
  • ddp - поменяйте местами вторую и третью строку
  • j - перейти к четвертой строке, т.е. к первой из следующих трех строк
  • q - остановить запись
  • @q - повторить макрос один раз
  • 3 @ q - повторить макрос три раза
Edheldil
источник
1
Вместо ручного повторения @q @q @qможно делать таким образом 3@q- повторить три раза. 100@q- повторить макрос 100 раз.
MiniMax
0

Использование: ./shuffle_lines.awk input.txt

Проверьте shebang #!/usr/bin/awk -f, потому что awkместоположение может отличаться в вашей системе.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
MiniMax
источник