Comment inverser toutes les 4 lignes?

13

Tout d'abord, étant donné que c'est mon premier message ici, je voudrais juste dire que j'ai trouvé que VIM est un excellent outil et que le forum ici est très utile pour trouver des réponses aux questions, avec beaucoup de gens utiles qui fournissent une aide inestimable. Je suis encore très nouveau sur VIM, donc à peu près tout ce que j'ai appris à ce sujet est venu d'ici.

Ma question est: je sais comment inverser TOUTES les lignes d'un fichier (: g / ^ / m0 entre autres), mais existe-t-il un moyen d'inverser toutes les 4 lignes d'un fichier, de sorte que

line1
line2
line3
line4
line5
line6
line7
line8
...

devient

line4
line3
line2
line1
line8
line7
line6
line5
...

Vous pouvez supposer qu'il y aura toujours un multiple exact de 4 lignes dans ces fichiers.

ablewasiereisawelba
la source
2
À propos de votre "ps": si vous insérez 4 espaces devant une ligne, cela est interprété comme du code (vous pouvez sélectionner le texte et utiliser les boutons de formatage en haut). Vous pouvez trouver plus de détails sur l' icône d'interrogation en haut .
mMontu

Réponses:

15

La commande :Reverseexpliquée sur Vim Wiki peut être utilisée pour cela (vous pouvez l'inclure dans votre .vimrcpour la rendre permanente):

command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl

Ensuite, vous pouvez enregistrer une macro pour exécuter la commande sur toutes les quatre lignes:

qmV3j:Reverse<cr>4jq
1000@m

Explication:

  • qm: 'q' en mode normal démarre (et arrête) l'enregistrement d'une macro dans un registre donné (registre 'm', dans ce cas, mais il peut s'agir de n'importe quelle autre lettre)
  • V: passer en mode visuel et sélectionner la ligne courante
  • 3j: étendre la sélection visuelle aux 3 lignes suivantes
  • :Reverse<cr>: exécutez la commande Inverser sur les lignes sélectionnées ( <cr>ici représente la enterclé)
  • 4j: passer aux lignes suivantes inchangées
  • q: arrête l'enregistrement de la macro
  • 1000@m: exécutez la macro enregistrée au registre 'm' 1000 fois (vous pouvez augmenter ce nombre si votre fichier est supérieur à 4000 lignes)

Éditer:

Comme mentionné dans les commentaires, vous pouvez utiliser une macro récursive au lieu d'utiliser un nombre:

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM: si le registre spécifié pour qest en majuscule, la macro est ajoutée au registre (ce qui est également utile lorsque vous réalisez que vous avez manqué les dernières étapes d'une macro complexe)
  • @m: exécuter la macro sur le registre m
  • q: arrêtez d'ajouter la macro

Bien qu'il soit créé en tant que macro, vous pouvez créer une commande pour cela s'il s'agit d'une tâche courante sur votre flux de travail:

function! Reverse4()
   let reg_m = @m
   let @m = '<c-r><c-r>m'
   normal! @m
   let @m = reg_m
endfunction
command! Reverse4 call Reverse4()
  • function! Reverse4()/ endfunction: définit une nouvelle fonction
  • let reg_m = @m: enregistre le contenu actuel du registre m
  • let @m = "<c-r><c-r>m": insérez la macro dans le registre m- notez que <c-r>c'est la notation vim pour Ctrl+ ret que vous devez taper ceci, pas copier / coller, donc votre ligne sera similaire à let @m = 'V3j:Reverse^M4j@m'et elle contiendra un caractère spécial (^ M)
  • normal! @m: la commande normale exécute son argument tel qu'il a été tapé en mode normal, il exécutera donc la macro récursive
  • let @m = reg_m: restaure le contenu du registre m, vous n'avez donc pas à vous souvenir que ce registre est utilisé sur cette fonction et évitez de l'utiliser

  • command! Reverse4 call Reverse4(): créer une nouvelle commande pour cette fonction

Selon vos besoins, vous pouvez l'améliorer, par exemple: passer un argument à la commande et à la fonction pour que cela fonctionne pour n'importe quel nombre de lignes au lieu d'être fixé par groupe de 4 lignes.

mMontu
la source
Au lieu du 1000@qhack, vous pouvez également utiliser une macro récursive: qmqqm ... @mq@m.
Poignée de porte
Quand j'ai essayé d'exécuter le "qmV3j: Reverse <cr> 4jq", il a dit "E492: Pas une commande d'éditeur". Je dois faire quelque chose de mal. J'utilise - et je n'ai jamais utilisé - GVIM, soit dit en passant. J'aurais dû le mentionner en premier lieu.
ablewasiereisawelba
Ah laisse tomber. Je l'ai compris - je ne suis pas censé taper ":" avant la partie "qmV3j: Reverse <cr> 4jq". Ça a marché! Merci beaucoup, mMontu. Si je peux demander - comment puis-je inclure la commande ": Reverse" dans mon .vimrc?
ablewasiereisawelba
3
@ablewasiereisawelba Pour rendre la commande disponible en permanence, écrivez simplement la ligne command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohlquelque part dans votre vimrc.
saginaw
@saginaw Oh, je ne savais pas que c'était si simple. Je vous remercie.
ablewasiereisawelba
9

Comme pour toutes les actions répétitives , si vous pouvez faire quelque chose une fois avec des opérations d'édition de base, vous pouvez facilement le faire plusieurs fois en enregistrant une macro.

(Dans ce cas, je vais utiliser une macro récursive, mais vous pouvez simplement enregistrer une macro non récursive et la lire plusieurs fois en martelant @@ou en utilisant un compte.)

ggqqqqqddjjpkddkkPjddpjj@qq@q

En panne

  1. gg: Passer au début du fichier.
  2. qqq: Effacer le registre q. Nous verrons pourquoi cela est nécessaire à l'étape 5.
  3. qq: Démarrer l'enregistrement d'une macro pour s'enregistrer q
  4. ddjjpkddkkPjddpjj: Une série d'opérations qui réorganise les quatre premières lignes simplement en les supprimant et en les collant manuellement. (Cela peut sembler compliqué à première vue, mais ne soyez pas trompé c'est quelque chose de très basique que vous auriez appris dans les 5 premières minutes environ de. vimtutor: j, k, dd, p, P)
  5. @q: Appelez la macro! À ce stade, le registre qne contient rien (car nous l'avons effacé à l'étape 2), donc rien ne se passera.
  6. q: Arrêtez l'enregistrement de la macro.
  7. @q: Relisez la macro récursive autant de fois que nécessaire.

NB Ce qui précède ne produira pas les résultats souhaités si le fichier contient un nombre de lignes qui n'est pas exactement divisible par quatre. Afin d'arrêter la macro tôt s'il ne reste pas quatre lignes, nous devons exécuter une commande en mode normal Vim qui échouera, avant de commencer à appliquer les modifications à l'étape 4. Nous pouvons le faire en essayant de descendre une ligne trois fois (puis sauvegarder) avant de commencer à déplacer les lignes:jjj3k

Donc:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q
Riches
la source
Pourquoi effacez-vous explicitement le registre q? Si vous enregistrez juste dedans, tout ce qui s'y trouve sera de toute façon écrasé ....
Wildcard
4
@Wildcard Parce que c'est une macro récursive, la première fois que vous appelez le contenu du registre q, il doit être vide, sinon il gâchera vos éditions.
saginaw
Très agréable. J'ai essayé cela de manière interactive sans essayer de taper la séquence exacte de votre commande et je me suis retrouvé en utilisant:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
Wildcard
1
@ablewasiereisawelba where I can just use the one-line custom command each time I need to do this- notez que vous n'avez pas à recréer la macro chaque fois que vous en avez besoin, car il est possible de la stocker en tant que fonction / commande sur votre vimrc.
mMontu
1
@ablewasiereisawelba Je suis content que vous ayez trouvé le "Edit" utile! Comme c'était votre premier message ici, vous ne savez peut-être pas comment fonctionnent les notifications StackExchange: lorsque vous postez un commentaire, l'auteur de la réponse / des questions reçoit une notification; les autres personnes ne recevront une notification que si vous incluez le @ <nick>. Ainsi, seul Rich a reçu des notifications concernant vos derniers messages.
mMontu
7

Vous pouvez également le faire avec une Excommande utilisant sedcomme filtre externe:

:%!sed -n 'h;n;G;h;n;G;h;n;G;p'

Cette version ignorera (supprimera) toutes les lignes supplémentaires au-delà d'un multiple de 4. Pour conserver le dernier ensemble de moins de 4 lignes (inversé), utilisez:

:%!sed -n '$p;h;n;G;$p;h;n;G;$p;h;n;G;p'

Le %moyen ici « Chaque ligne du tampon. »

La !commande signifie «Exécutez la commande suivante avec les lignes spécifiées en entrée et remplacez les lignes spécifiées par la sortie de la commande». (Cela s'appelle un filtre; très pratique pour des choses comme le tri, par exemple, :%!sorttriera toutes les lignes de votre fichier; :2,8!sorttriera les lignes 2 à 8, etc.)

sedest l' outil d' édition de flux et se trouve sur tous les systèmes de type Unix. Les concepts clés sedutilisés ici sont l '"espace modèle" (qui, par défaut, contient uniquement chaque ligne de l'entrée à son tour), et l' "espace d'attente" (qui est l'endroit où vous pouvez coller du texte supplémentaire lors de l'utilisation sedpour l'enregistrer lors du traitement d'autres lignes d'entrée).

-nest une option de la sedcommande pour supprimer ses actions par défaut d'impression de l'espace de motif (car dans ce cas, nous ne voulons imprimer que lorsque nous le disons explicitement.)

$pdans la sedcommande signifie "Si vous êtes sur la dernière ligne de sedsaisie, imprimez (l'espace de motif)."

h signifie "coller le contenu actuel de l '" espace modèle "dans" l'espace d'attente ", en écrasant tout ce qui s'y trouve."

n signifie "remplacer le contenu de" l'espace modèle "par la ligne suivante de l'entrée."

G signifie "ajouter à l '" espace modèle ": une nouvelle ligne suivie du contenu de" l'espace d'attente "."

Dans l'ensemble, la sedcommande stocke quatre lignes de sortie, les inversant telles qu'elles les stockent, puis les imprime. Les $pcommandes ajoutées dans la deuxième version garantissent que si la dernière ligne du fichier est atteinte autrement que sur un multiple de 4 lignes, les lignes sont toujours imprimées.


Pour une approche alternative et interactive sans utiliser de fonctionnalités spécifiques à Vim et sans utiliser de filtre externe:

:4

pour aller à la quatrième ligne.

:.m -4 | +3m . | +2m . | +5

pour inverser les quatre lignes précédentes (1-4) et laissez votre curseur sur la ligne 8.

.m -4déplace la ligne actuelle juste après la ligne de quatre lignes en arrière (en laissant le curseur sur la ligne déplacée).

+3m .déplace la ligne qui se trouve 3 lignes après la ligne actuelle, juste après la ligne actuelle, en laissant le curseur sur la ligne déplacée. +2m .bien sûr fonctionne de la même façon.

+5 place le curseur à cinq lignes de l'endroit où il se trouve.

Répétez comme vous le souhaitez.

Dans Vim, vous pouvez répéter toute cette commande avec @:, puis recommencer avec @@. En Posix viou exvous devez insérer :.m -4 | +3m . | +2m . | +5 une ligne de texte, supprimer un tampon appelé (registre), puis exécuter ce tampon appelé (registre).

Donc en exmode, inverser les lignes de manière interactive en utilisant uniquement les fonctionnalités spécifiées par POSIX, et en commençant par 17 lignes de texte:

Entering Ex mode.  Type "visual" to go to Normal mode.
:0a     # Append following text after "line 0" (i.e. insert at start of file).
.m -4 | +3m . | +2m . | +5
.       # End text insertion
:d k    # Delete that line to register k
line1   # This is a printout of the current line
:4      # Move to line 4
line4
:@k     # Execute register k to reverse lines 1-4
line8
:@@     # Execute register k again
line12
:@@     # Execute register k again
line16
:@@     # Execute register k again
line17
:%p     # Print the whole buffer (just to see what was done)
line4
line3
line2
line1
line8
line7
line6
line5
line12
line11
line10
line9
line16
line15
line14
line13
line17
:wq     # Save and quit

Lectures complémentaires:

Caractère générique
la source
Pourriez-vous expliquer un peu plus votre solution?
vappolinario
3
@vappolinario, j'ai ajouté quelques explications supplémentaires. Est ce que ça aide? :)
Wildcard
Wow, très longue réponse. :) J'ai séduit, en fait, mais je viens de l'utiliser avec un script simple que j'ai trouvé quelque part - probablement sur ce forum - qui était une solution à un problème que j'avais. Cependant, lorsque j'ai essayé d'exécuter la ligne que vous avez suggérée, le message "sed" n'est pas reconnu comme une commande interne ou externe, un programme exploitable ou un fichier de commandes. " Je ne sais pas si j'ai besoin d'avoir sed dans le dossier vim ou quoi.
ablewasiereisawelba
1
@ablewasiereisawelba, si vous exécutez sur une boîte Windows et n'utilisez pas Cygwin (ou MobaXterm ou similaire), vous pouvez essayer l'autre méthode que j'ai donnée: 4G:.m -4 | +3m . | +2m . | +5<Enter>@:puis @@répétée jusqu'à ce que votre fichier soit entièrement traité. Je recommande cependant d'installer MobaXterm. :)
Wildcard
@Wildcard Oh d'accord, je vérifie MobaXterm maintenant. :)
ablewasiereisawelba
7
:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')

Anglais simple: pour chaque ligne, déplacez la ligne actuelle vers le haut lnum % 4, sauf lnum % 4 == 0dans ce cas, déplacez la ligne actuelle vers le haut 4.

De plus, pour inverser toutes les nlignes, remplacez le 4'dans la commande ci-dessus par n.

djjcast
la source
2
Très belle commande en ligne. Merci, djjcast.
ablewasiereisawelba