Zmień kolejność linii w pliku

11

Próbuję zmienić kolejność linii w określonym wzorze. Praca z plikiem zawierającym wiele linii (np. 99 linii). Dla każdych trzech linii chciałbym, aby druga linia była trzecią linią, a trzecią linią drugą.

PRZYKŁAD.

1- Wejście:

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

2-wyjściowe:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...
Annick Raymond
źródło

Odpowiedzi:

12

Korzystanie z awkmatematyki i liczb całkowitych:

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

Operator modułu wykonuje dzielenie liczb całkowitych i zwraca resztę, więc dla każdej linii zwróci sekwencję 1, 2, 0, 1, 2, 0 [...]. Wiedząc o tym, po prostu zapisujemy dane wejściowe w liniach, w których moduł wynosi 2 na później - to znaczy tuż po wydrukowaniu danych wejściowych, gdy jest zero.

DopeGhoti
źródło
Mamy tutaj małą wadę. Zobacz moją odpowiedź, drobna poprawa
Sergiy Kolodyazhnyy
Dzięki za dobry połów; Do mojej odpowiedzi włączyłem poprawkę w postaci 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

Oznacza to, że pprzeszukuj bieżącą linię, pobierz next, hstary, pobierz next, Gi przytrzymaj linię (dołącz do przestrzeni wzorów) i pzrewiduj tę 2-liniową przestrzeń wzorów z zamienionymi trzecią i drugą linią.

Stéphane Chazelas
źródło
3

Kolejne podejście awk :

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

Wyjście:

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- wyodrębnia 2 kolejne rekordy, jeśli istnieją

  • każdy drugi i trzeci rekord są przypisane odpowiednio do zmiennych L2i L3zmiennych

Roman Perekhrest
źródło
1
Zakładam, że te zmienne zaczynają się na literę L (małe litery). Są kiepskim wyborem dla czytelności, ponieważ wyglądają jak cyfry dla dwunastu i trzynastu. Lepszym wyborem może być line2itp.
Wstrzymano do odwołania.
@DennisWilliamson, zmieniono na wielkie litery
RomanPerekhrest
1

Korzystanie perli krótki skrypt:

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.

Skrypt przetwarza cały plik, dla każdej linii (zapisanej w $_) otrzyma kolejne dwie linie ( $l2i $l3) i wydrukuje je w żądanej kolejności: linia1, linia3, linia2.

Frank Förster
źródło
1

Jednym ze sposobów może być:

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

Alternatywnie,

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

Wyniki

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

źródło
1

Dlaczego nie zrobić pętli while? W rozszerzonej formie:

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

W „formacie jednowierszowym”:

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

Wyjścia:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
Stephen Quan
źródło
1

Perl

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

Chodzi o to, że używamy operatora modulo %ze $.zmienną numeru linii , aby dowiedzieć się, który z nich jest co pierwszy, a który co sekundę, a który co 3 linię. Dla każdej trzeciej linii pozostała wartość wynosi 0, natomiast dla każdej pierwszej i drugiej linii będzie mieć odpowiadające jej liczby.

Test:

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

Niewielka poprawa

Podejście polegające na przechowywaniu drugiej linii w zmiennej ma wadę. Co jeśli ostatni wiersz to „drugi”, tzn. Dla tego numeru reszta to 2? Oryginalny kod w mojej i DopeGhoti odpowiedzi nie zostanie wydrukowany, My dog is orangejeśli pominiemy ostatni wiersz. Rozwiązaniem tego problemu w obu przypadkach jest użycie END{}bloku kodu z rozbrajaniem zmiennej tymczasowej po wydrukowaniu. Innymi słowy:

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

i

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

W ten sposób kod będzie działał dla dowolnej liczby wierszy w pliku, nie tylko tych podzielnych przez 3.

Dodatkowa poprawka dotycząca problemu wymienionego w komentarzach

W przypadku awk, jeśli ostatni wiersz w pliku daje wynik 1 dla $. % 3, poprzedni kod miał problem z wyświetlaniem pustego znaku nowej linii z powodu bezwarunkowego drukowania END{print delay}, ponieważ printfunkcja wymieniona w komentarzach zawsze dołącza znak nowej linii do dowolnej zmiennej, na której działa. W przypadku perlwersji ten problem nie występuje, ponieważ funkcja z -neflagami printnie dołącza nowego wiersza.

Niemniej jednak poprawką w przypadku awk jest uzależnienie, jak wspomniał Dope Ghoti w komentarzach, aby zweryfikować długość zmiennej tymczasowej. Wersja tej samej poprawki dla Perla to:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 
Sergiy Kolodyazhnyy
źródło
1
Twoja poprawka ma potencjalną niewielką wadę polegającą na tym, że dołącza pusty wiersz danych wyjściowych dla plików o „niewłaściwej” liczbie wierszy. Naprawiłem to w swoim uwzględnieniu twojej poprawy w mojej odpowiedzi z (for awk) NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay }.
DopeGhoti
1
@DopeGhoti Problem nie występuje w Perlu, ponieważ druk Perla z -neflagami nie wyświetla nowej linii. Rzeczywiście drukuje, ale jest to ciąg zerowy, bez końca nowej linii. Niemniej jednak do mojej odpowiedzi dodałem wzmiankę o tym problemie i tę samą poprawkę. Dzięki !
Sergiy Kolodyazhnyy
1

Wigor

Nie nadaje się do długich plików, ale nadal jest przydatny, jeśli właśnie edytowałeś plik i chciałeś na przykład zmienić kolejność niektórych yamlowych zwrotek.

Najpierw zapisz makro:

gg qq j ddp j q

A następnie powtórz żądaną liczbę razy:

@q @q @q ...

Lub po prostu np

3@q

Wyjaśnienie:

  • gg - przejdź do pierwszej linii
  • qq - rozpocznij rejestrowanie makra
  • j - przejdź do drugiej linii
  • ddp - zamień drugą i trzecią linię
  • j - przejdź do czwartej linii, tj. do pierwszej z kolejnych trzech linii
  • q - zatrzymaj nagrywanie
  • @q - powtórz makro raz
  • 3 @ q - powtórz makro trzy razy
Edheldil
źródło
1
Zamiast ręcznego powtarzania @q @q @qmożna to zrobić w ten sposób 3@q- powtórz trzykrotnie. 100@q- powtórz makro 100 razy.
MiniMax
0

Stosowanie: ./shuffle_lines.awk input.txt

Sprawdź shebang #!/usr/bin/awk -f, ponieważ awklokalizacja może się różnić w twoim systemie.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
MiniMax
źródło