Jak odwrócić kolejność linii?


24

Jak mogę odwrócić kolejność linii, aby pierwsza linia pojawiła się na końcu, a ostatnia linia była pierwsza? (Mogą to być wszystkie wiersze w buforze, zakres adresów lub wybór trybu wizualnego linii).

Chciałbym się przekształcić

rat
ox
tiger
⋮
dog
pig

w

pig
dog
⋮
tiger
ox
rat

bez uciekania się do zewnętrznego polecenia, takiego jak tac.


Wszelkie sugestie dotyczące lepszych tagów na to pytanie?
200_success

1
może nowy tag „pure-vi” lub podobny? Widziałem kilka pytań, które skorzystałyby z tagu, który wskazywałby na chęć nie angażowania zewnętrznych narzędzi. Czy powinienem o to pytać w Meta?
John O'M.

1
@Carpetsmoker (i wszyscy inni zainteresowani śledzeniem tego) pytanie o tag jest teraz na meta.vi.stackexchange.com/questions/1229/…
John O'M.

Odpowiedzi:


29

Siła globalna będzie działać tutaj:

:g/^/exe "normal ddggP"

Lub prościej (dzięki @tommcdo)

:g/^/move 0

Pierwszy dopasuje każdą linię i dla każdej linii usunie ją i wklei na górze pliku. Gdy przesuwa się po pliku, odwraca tekst.

Drugi podobnie pasuje do każdej linii i przenosi ją na początek pliku.

Uwaga: Oba działają na całym pliku i nie będą poprawnie stosowane do odwracania podzbioru linii. Zobacz odpowiedź Ingo Karkat na rozwiązanie, które działa w określonym zakresie.

Opis:

gpolecenie globalne
/^/pasuje do dowolnego wiersza, który ma początek (tj. wszystkie wiersze)
exewykonaj następujący ciąg
"normalwykonaj polecenia w trybie normalnym
ddusuń
ggprzejście do góry pliku
Pwklej powyżej aktualnej pozycji

move 0 przenosi bieżącą linię poniżej linii 0 (co umieszcza ją w pozycji 1 lub pierwszej linii pliku)


6
Zamiast :normalpolecenia możemy użyć polecenia Ex :move 0, które przenosi linię na początek bufora.
tommcdo

1
:executeJest to również konieczne tylko wtedy, gdy polecenie musi być budowane dynamicznie, np :execute 'normal' g:user_command.
tommcdo

@tommcdo dobre punkty! Mam w zwyczaju używać, :executeponieważ często kończę na dodawaniu innych poleceń Ex po istniejącym później, a wygodniej jest dla mnie mieć je :exetam, niż wracać i wstawiać je później. Niestety, nawyk ten wyciekł do tej odpowiedzi, gdzie nie dotyczy tak wiele.
John O'M.

1
Więcej wyjaśnień na temat mojego użycia :execute: ponieważ zajmuje ciąg, zapewnia jasne określenie, gdzie kończą się polecenia trybu normalnego, mimo że nie buduję łańcucha, łatwiej jest mi znaleźć zrównoważone znaki cudzysłowu niż szukać <esc>lub cokolwiek, aby zakończyć tryb. Ponownie jest to osobiste preferencje i nawyk. :-)
John O'M.

3
Działa to dla zakresu btw: :9,11g/^/move 8... Ostatnią liczbą musi być początek zakresu minus 1 (na podstawie odpowiedzi Ingo).
Martin Tournoij

13

Ta linijka (dla ciebie ~/.vimrc) definiuje :Reversepolecenie; możesz również użyć tej :globalczęści bezpośrednio, ale składnia :move(która iteracyjnie przesuwa linie do początku zakresu, a tym samym odwraca go) nie jest łatwa do zapamiętania:

:command! -bar -range=% Reverse <line1>,<line2>global/^/m<line1>-1

1
Jako FYI dla czytelników, <line1>& <line2>są zobowiązane do tego, aby działało w pewnym zakresie, tj .: :7,9Reverse(są to cechy command, nie globallub move). Prostsze :command! -bar -range=% Reverse :global/^/m 0będzie również działać, ale tylko dla całego bufora ...
Martin Tournoij

6

Pure Vim:

:g/^/m0

Wyjaśnienie:

Według :help multi-repeat, :ga jego kuzyn :vpracuje w sposób dwa przejścia.

Pierwszy przebieg :gzaznacza każdą pasującą linię {pattern}, a drugi przebieg (najwyraźniej wykonywany od początku pliku i przechodzący do końca) wykonuje [cmd]. Powyższe użycie :gkorzysta z kolejności, w której linie są przetwarzane (co prawdopodobnie jest w porządku, choć prawdopodobnie nie jest technicznie gwarantowane).

Działa najpierw poprzez zaznaczenie każdej linii, następnie przesunięcie pierwszej zaznaczonej linii na górę pliku, a następnie przesunięcie drugiej na górę pliku (powyżej linii poprzednio przeniesionej), a następnie trzeciej zaznaczonej linii (ponownie powyżej poprzednio przeniesionej linii) i tak dalej, aż ostatnia linia w pliku zostanie przeniesiona na górę, skutecznie odwracając plik.

Zauważ, że jeśli :gprzetworzone wiersze w dowolnej kolejności innej niż od góry do dołu, to polecenie nie będzie działać.

Źródło: Odwróć wszystkie linie i Moc g na vim wikia.

Kilka przykładów użycia zewnętrznych poleceń:

  • tac(część GNU coreutils - catodwrócona):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail na BSD / OSX (niezgodny z POSIX):

    :%!tail -r
    

    -r Opcja -r powoduje, że dane wejściowe są wyświetlane w odwrotnej kolejności, według linii.

    Sprawdź: man tarwięcej szczegółów.

Aby uzyskać więcej pomysłów, zobacz:


2
Czy to nie :g/^/m0to samo :g/^/move 0, co odpowiada John?
muru

@muru Myślę, że tak, ale ten jest krótszy (zgodnie z vim wiki) i dodałem inne wyjaśnienie z kilkoma dodatkowymi przykładami korzystania z linii poleceń.
kenorb

Tak, głosowałem za innymi poleceniami (również przyszedłem pisać tac). Ale podejrzewam, że głosowanie było spowodowane powtórzeniem odpowiedzi.
muru

Wiem, że taczostało to wspomniane przez OP, ale wszystkie inne podobne pytania i tak byłyby takie same, więc warto o tym wspomnieć jeszcze raz. John wziął ten cmd z komentarza @tommcdo, początkowo wziąłem go z DerMike , ale myślę, że wziął go po prostu z Wikii, więc dałem kredyt vim Wikii, więc nie jest całkowicie duplikowany, ponieważ wyjaśnienie jest zupełnie inne.
kenorb

Dodaje więcej wartości, ponieważ jest o wiele krótszą wersją z odpowiednim wyjaśnieniem, a także przypisuję odpowiednie źródła. Używanie poleceń powłoki jest bardzo proste i wygodne. Jeśli ludzie się nie zgadzają, mogą po prostu obniżyć głosowanie, nic wielkiego.
kenorb

6

W duchu funkcjonalnego VimL:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))zwraca listę wszystkich linii w buforze. '$'jest specjalnym argumentem, dla line()którego wskazuje ostatni wiersz w buforze.
  • reverse(...)odwraca listę wejściową w miejscu. Trzeba by użyć, reverse(copy(...))jeśli lista wejściowa nie powinna być modyfikowana.
  • setline(1, ...)zastępuje określoną linię drugim argumentem. Gdy drugim argumentem jest lista, ta sama liczba wierszy co długość listy zostaje zastąpiona zawartością listy.

Jeśli chcesz, możesz także zdefiniować polecenie, które ma zakres (domyślny %cały bufor)

:command! -bar -range=% Reverse call setline(<line1>, reverse(getline(<line1>, <line2>)))

1
Podoba mi się ta odpowiedź. Nie wyróżnia też elementów (jeśli hlsearchjest włączone), takich jak :g/polecenie z innych odpowiedzi ... Może wydajność jest jednak gorsza? Ponieważ getline(1, line('$'))pobiera cały bufor do pamięci. reverse()wydaje się być na miejscu, więc powinno to zająć bardzo mało pamięci jako takiej ...
Martin Tournoij

3

Dokumentacja Vima usr_12.txt - Sprytne sztuczki

12.4 Odwrotna kolejność linii

:globalKomenda może być połączona z :movepoleceniem przenieść wszystkie linie przed pierwszym wierszu w wyniku odwrotnej pliku. Polecenie to:

:global/^/m 0

W skrócie:

:g/^/m 0

^Wyrażenie regularne dopasowuje początek linii (nawet jeśli linia jest pusta). :movePolecenie przesuwa linię pasujący do linii po mitycznym zerowego, więc obecna linia dopasowanie staje się pierwszą linię w pliku. Ponieważ :globalpolecenie nie jest mylone przez zmianę numeracji wierszy, :globaldopasowuje wszystkie pozostałe wiersze pliku i umieszcza każdy z nich jako pierwszy.

Działa to również na szeregu linii. Najpierw przejdź do pierwszej linii i zaznacz ją mt. Następnie przesuń kursor do ostatniej linii w zakresie i wpisz:

:'t+1,.g/^/m 't

1

Używanie liczb względnych. Akapit zaczyna się od linii 13 i wysyła więcej 4 linii

 :13,13+4g/^/m12
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.