Comment inverser l'ordre des lignes?

24

Comment inverser l'ordre des lignes pour que la première ligne apparaisse à la fin et la dernière ligne apparaisse en premier? (Il peut s'agir de toutes les lignes d'un tampon, d'une plage d'adresses ou d'une sélection de mode visuel en ligne.)

Je voudrais transformer

rat
ox
tiger
⋮
dog
pig

dans

pig
dog
⋮
tiger
ox
rat

sans recourir à une commande externe telle que tac.

200_success
la source
Des suggestions pour de meilleurs tags sur cette question?
200_success
1
peut-être une nouvelle balise 'pure-vi' ou similaire? J'ai vu plusieurs questions qui bénéficieraient d'une balise qui indiquerait un désir de n'avoir aucun outil externe impliqué. Dois-je poser des questions à ce sujet sur Meta?
John O'M.
1
@Carpetsmoker (et toute autre personne intéressée à suivre ceci), la question des balises est maintenant sur meta meta.vi.stackexchange.com/questions/1229/…
John O'M.

Réponses:

29

Le pouvoir du mondial fonctionnera ici:

:g/^/exe "normal ddggP"

Ou, plus simplement (merci @tommcdo)

:g/^/move 0

Le premier correspondra à chaque ligne et pour chaque ligne, supprimez-le et collez-le en haut du fichier. En se déplaçant dans le fichier, il inverse le texte.

La seconde correspond de manière similaire à chaque ligne et la déplace vers le haut du fichier.

Remarque: les deux fonctionnent sur l' ensemble du fichier et ne s'appliqueront pas correctement à l'inversion d'un sous-ensemble de lignes. Voir la réponse d'Ingo Karkat pour une solution qui fonctionne dans une plage.

La description:

gcommande globale
/^/correspond à n'importe quelle ligne qui a un début (c'est-à-dire toutes les lignes)
exeexécute la chaîne suivante
"normalexécute les commandes en mode normal
ddsupprime la ligne
ggdéplace vers le haut du fichier
Pcolle au-dessus de la position actuelle

move 0 déplace la ligne actuelle en dessous de la ligne 0 (ce qui la place à la position 1 ou à la première ligne du fichier)

John O'M.
la source
6
Au lieu de la :normalcommande, nous pouvons utiliser la commande Ex :move 0, qui déplace la ligne au début du tampon.
tommcdo
1
Est également :executenécessaire uniquement lorsque la commande doit être construite dynamiquement, par exemple :execute 'normal' g:user_command.
tommcdo
@tommcdo bons points! J'ai l'habitude d'utiliser :executecar je finis souvent par ajouter d'autres commandes Ex après celle existante plus tard, et il est plus pratique pour moi d'y avoir :exedéjà que d'avoir à revenir en arrière et à l'insérer plus tard. Malheureusement, cette habitude s'est infiltrée dans cette réponse où elle ne s'applique pas autant.
John O'M.
1
Plus d'explications sur mon utilisation de :execute: comme il prend une chaîne, il fournit une délimitation claire de l'endroit où se terminent les commandes en mode normal, même si je ne construis pas la chaîne, il est plus facile pour moi de trouver des guillemets équilibrés que de chercher <esc>ou quoi que ce soit pour terminer le mode. Encore une fois, c'est une préférence et une habitude personnelles. :-)
John O'M.
3
Cela fonctionnera pour une plage btw: :9,11g/^/move 8... Le dernier nombre doit être le début de la plage moins 1 (adapté de la réponse d'Ingo).
Martin Tournoij
13

Ce one-liner (pour votre ~/.vimrc) définit une :Reversecommande; vous pouvez également utiliser la :globalpartie directement, mais la syntaxe de la :move(qui déplace itérativement les lignes avant le début de la plage, l'inversant ainsi) n'est pas facile à mémoriser:

:command! -bar -range=% Reverse <line1>,<line2>global/^/m<line1>-1
Ingo Karkat
la source
1
En tant que FYI pour les lecteurs, le <line1>& <line2>est nécessaire pour que cela fonctionne sur une plage, c'est-à-dire: :7,9Reverse(ce sont des fonctionnalités de command, pas globalou move). Le plus simple :command! -bar -range=% Reverse :global/^/m 0fonctionnera également, mais uniquement pour la totalité du tampon ...
Martin Tournoij
6

Pure Vim:

:g/^/m0

Explication:

Selon :help multi-repeat, :get son cousin :vtravaillent en deux temps.

La première passe :gmarque chaque correspondance de ligne {pattern}, tandis que la deuxième passe (apparemment effectuée à partir du début du fichier et se poursuivant jusqu'à la fin) effectue la [cmd]. L'utilisation ci-dessus :gtire parti de l'ordre dans lequel les lignes sont traitées (ce qui est probablement correct, mais probablement pas techniquement garanti).

Cela fonctionne en marquant d'abord chaque ligne, puis en déplaçant la première ligne marquée vers le haut du fichier, puis en déplaçant la seconde en haut du fichier (au-dessus de la ligne déplacée précédemment), puis la troisième ligne marquée (à nouveau au-dessus de la précédemment déplacée). ligne), et ainsi de suite jusqu'à ce que la dernière ligne du fichier soit déplacée vers le haut, inversant ainsi le fichier.

Notez que si :gles lignes sont traitées dans un ordre autre que de haut en bas, cette commande ne fonctionnera pas.

Source: inversez toutes les lignes et la puissance de g sur wikia vim.

Quelques exemples d'utilisation de commandes externes:

  • tac(partie de GNU coreutils - catinversée):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail sous BSD / OSX (non compatible POSIX):

    :%!tail -r
    

    -r L'option -r provoque l'affichage de l'entrée dans l'ordre inverse, par ligne.

    Vérifiez: man tarpour plus de détails.

Pour plus d'idées, voir:

kenorb
la source
2
N'est-ce pas :g/^/m0la même chose que :g/^/move 0la réponse de John?
muru
@muru Je pense que oui, mais celui-ci est plus court (selon vim wikia) et j'ai ajouté des explications différentes avec quelques exemples supplémentaires d'utilisation des lignes de commande.
kenorb
Oui, j'ai voté à cause des autres commandes (je suis aussi venu poster tac). Mais je soupçonne que le downvote était dû à la répétition de la réponse.
muru
Je suis conscient que cela a tacété mentionné par OP, mais toutes les autres questions similaires seraient en double de toute façon, il est donc bon de le mentionner à nouveau. John a pris cette cmd du commentaire @tommcdo, je l'ai d'abord pris de DerMike , mais je pense qu'il l'a simplement pris de wikia, j'ai donc donné des crédits à vim wikia, donc ce n'est pas complètement en double car l'explication est complètement différente.
kenorb
Cela ajoute plus de valeur, car c'est une version beaucoup plus courte avec des explications appropriées et je crédite également les bonnes sources. L'utilisation des commandes shell est très simple et pratique. Si les gens ne sont pas d'accord, ils peuvent simplement voter contre, ce n'est pas grave.
kenorb
6

Dans l'esprit du VimL fonctionnel:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))renvoie une liste de toutes les lignes du tampon. '$'est un argument spécial pour line()lequel indique la dernière ligne du tampon.
  • reverse(...)inverse la liste d'entrée, sur place. Il faudrait utiliser reverse(copy(...))si la liste d'entrée ne devait pas être modifiée.
  • setline(1, ...)remplace la ligne spécifiée par le deuxième argument. Lorsque le deuxième argument est une liste, le même nombre de lignes que la longueur de la liste est remplacé par le contenu de la liste.

Si vous le souhaitez, vous pouvez également définir une commande qui prend une plage ( %tampon entier par défaut )

:command! -bar -range=% Reverse call setline(<line1>, reverse(getline(<line1>, <line2>)))
jamessan
la source
1
J'aime cette réponse. Il ne met pas non plus en évidence des éléments (si hlsearchest activé) comme la :g/commande des autres réponses ... Les performances sont peut-être pires, cependant? Puisqu'il getline(1, line('$'))obtient le tampon entier en mémoire. reverse()semble être en place, donc cela devrait prendre très peu de mémoire en tant que tel ...
Martin Tournoij
3

Selon la documentation Vim usr_12.txt - Astuces astucieuses

12.4 Ordre des lignes inversées

La :globalcommande peut être combinée avec la :movecommande pour déplacer toutes les lignes avant la première ligne, ce qui entraîne un fichier inversé. La commande est:

:global/^/m 0

Abrégé:

:g/^/m 0

L' ^expression régulière correspond au début de la ligne (même si la ligne est vide). La :movecommande déplace la ligne correspondante après la ligne zéro mythique, de sorte que la ligne correspondante actuelle devient la première ligne du fichier. Comme la :globalcommande n'est pas confondue par la numérotation des lignes changeante, :globalprocède à faire correspondre toutes les lignes restantes du fichier et place chacune comme première.

Cela fonctionne également sur une gamme de lignes. Déplacez-vous d'abord au-dessus de la première ligne et marquez-la avec mt. Déplacez ensuite le curseur sur la dernière ligne de la plage et saisissez:

:'t+1,.g/^/m 't
jecxjo
la source
1

Utilisation de nombres relatifs. Le paragraphe commence à la ligne 13 et spams plus de 4 lignes

 :13,13+4g/^/m12
SergioAraujo
la source