Змініть порядок рядків у файлі


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 для подальшого - дотепно, одразу після друку введення, коли він дорівнює нулю.


У нас тут невеликий недолік. Дивіться мою відповідь, незначна частина покращення
Сергій Колодяжний

Дякую за хороший улов; Я включив виправлення у свою відповідь у формі 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оберніть поточний рядок, nдістаньте ext, hстарий, nдістаньте ext, 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змінні відповідно


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

Ідея тут полягає в тому, що ми використовуємо оператор модуля %зі $.змінною номера рядка , щоб визначити, який з них кожен перший, який - кожен другий, а який - кожен 3-й рядок. Для кожного третього рядка залишок дорівнює 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 полягає в тому, щоб зробити умовою, як згадує Допі Готі в коментарях, - це перевірити довжину тимчасової змінної. Версія 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

Вим

Не підходить для довгих файлів, але все ще зручно, якщо ви просто редагували файл і хотіли, наприклад, змінити порядок ямл-строф.

Спочатку запишіть макрос:

gg qq j ddp j q

А потім повторіть потрібну кількість разів:

@q @q @q ...

Або просто напр

3@q

Пояснення:

  • gg - перехід до першого рядка
  • qq - почати запис макросу
  • j - перейти до другого рядка
  • ddp - поміняти місцями другий та третій рядки
  • j - перейти до четвертого рядка, тобто до першого з наступних трьох рядків
  • q - зупинити запис
  • @q - повторно відтворити макрос
  • 3 @ q - повторно повторити макрос

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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.