Come invertire l'ordine delle linee?


24

Come posso invertire l'ordine delle linee in modo che la prima riga appaia alla fine e l'ultima riga appaia per prima? (Potrebbe trattarsi di tutte le righe in un buffer, un intervallo di indirizzi o una selezione della modalità visiva lineare).

Vorrei trasformarmi

rat
ox
tiger
⋮
dog
pig

in

pig
dog
⋮
tiger
ox
rat

senza ricorrere a un comando esterno come tac.


Qualche suggerimento per tag migliori su questa domanda?
200_successo

1
forse un nuovo 'pure-vi' o tag simile? Ho visto diverse domande che trarrebbero beneficio da un tag che indicherebbe il desiderio di non avere strumenti esterni coinvolti. Dovrei chiedere a riguardo su Meta?
John O'M.

1
@Carpetsmoker (e chiunque altro sia interessato a seguirlo) la domanda del tag è ora su meta meta.vi.stackexchange.com/questions/1229/…
John O'M.

Risposte:


29

Il potere del globale funzionerà qui:

:g/^/exe "normal ddggP"

O, più semplicemente (grazie @tommcdo)

:g/^/move 0

Il primo corrisponderà a ogni riga e per ogni riga, eliminalo e incollalo nella parte superiore del file. Mentre si sposta attraverso il file, inverte il testo.

Il secondo corrisponde in modo simile a ogni riga e lo sposta nella parte superiore del file.

Nota: entrambi funzionano sull'intero file e non si applicheranno correttamente all'inversione di un sottoinsieme delle righe. Vedi la risposta di Ingo Karkat per una soluzione che funziona in un intervallo.

Descrizione:

gil comando globale
/^/corrisponde a qualsiasi riga che ha un inizio (ovvero tutte le righe)
exeesegue la seguente stringa
"normalesegue i comandi in modalità normale
ddelimina riga
ggsposta all'inizio del file
Pincolla sopra la posizione corrente

move 0 sposta la riga corrente al di sotto della riga 0 (che la mette in posizione 1 o la prima riga del file)


6
Invece del :normalcomando, possiamo usare il comando Ex :move 0, che sposta la riga all'inizio del buffer.
Tommaso,

1
Inoltre :executeè necessario solo quando il comando deve essere creato in modo dinamico, ad es :execute 'normal' g:user_command.
martedì

@tommcdo punti positivi! Ho l'abitudine di usarlo :executeperché finisco spesso per aggiungere altri comandi Ex dopo quello esistente più tardi, ed è più conveniente per me avere :exegià lì che dover tornare indietro e inserirlo successivamente. Sfortunatamente, quell'abitudine trapelò in questa risposta dove non si applica così tanto.
John O'M.

1
Altre spiegazioni sul mio uso di :execute: poiché richiede una stringa, fornisce una chiara delineazione di dove finiscono i comandi in modalità normale, anche se non sto costruendo la stringa, per me è più facile trovare virgolette bilanciate che cercare <esc>o qualunque cosa per terminare la modalità. Ancora una volta, questa è preferenza e abitudine personale. :-)
John O'M.

3
Questo funzionerà per un intervallo tra: :9,11g/^/move 8... L'ultimo numero deve essere l'inizio dell'intervallo meno 1 (adattato dalla risposta di Ingo).
Martin Tournoij,

13

Questo one-liner (per il tuo ~/.vimrc) definisce un :Reversecomando; puoi anche usare :globaldirettamente la parte, ma la sintassi di :move(che sposta ripetutamente le linee prima dell'inizio dell'intervallo, invertendolo in tal modo) non è facile da memorizzare:

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

1
In quanto FYI per i lettori, è necessario che <line1>& <line2>faccia funzionare questo su una gamma, cioè: :7,9Reverse(sono caratteristiche di command, non globalo move). Anche il più semplice :command! -bar -range=% Reverse :global/^/m 0funzionerà, ma solo per l'intero buffer ...
Martin Tournoij,

6

Pure Vim:

:g/^/m0

Spiegazione:

Secondo :help multi-repeat, :ge suo cugino :vlavorano in due modi.

Il primo passaggio :gsegna ogni corrispondenza della riga {pattern}, mentre il secondo passaggio (apparentemente eseguito a partire dall'inizio del file e procedendo fino alla fine) esegue il [cmd]. L'uso di cui sopra :gsfrutta l'ordine in cui le linee vengono elaborate (il che probabilmente va bene, anche se probabilmente non è tecnicamente garantito).

Funziona contrassegnando prima ogni riga, quindi spostando la prima riga contrassegnata nella parte superiore del file, quindi spostando la seconda nella parte superiore del file (sopra la riga spostata in precedenza), quindi la terza riga contrassegnata (di nuovo sopra la precedente spostata line) e così via fino a quando l'ultima riga del file viene spostata in alto, invertendo efficacemente il file.

Se le :glinee elaborate in un ordine diverso da quello dall'alto verso il basso, questo comando non funzionerebbe.

Fonte: inverti tutte le linee e la potenza di g su vim wikia.

Alcuni esempi usando i comandi esterni:

  • tac(parte dei coreutils GNU - catinvertiti):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail su BSD / OSX (non conforme a POSIX):

    :%!tail -r
    

    -r L'opzione -r fa sì che l'ingresso venga visualizzato in ordine inverso, per riga.

    Verifica: man tarper maggiori dettagli.

Per altre idee, vedi:


2
Non è :g/^/m0la stessa :g/^/move 0, qual è la risposta di John?
muru,

@muru Penso di sì, ma questo è più breve (secondo vim wikia) e ho aggiunto spiegazioni diverse con alcuni esempi aggiuntivi sull'uso delle righe di comando.
Kenorb,

Sì, ho effettuato l'upgrade a causa degli altri comandi (sono venuto anche a pubblicare tac). Ma sospetto che il downvote sia dovuto alla ripetizione della risposta.
muru,

Sono consapevole che è tacstato menzionato da OP, ma tutte le altre domande simili ne sarebbero comunque duplicate, quindi è bene menzionarlo di nuovo. John ha preso questo cmd dal commento di @tommcdo, l'ho preso inizialmente da DerMike , ma penso che lo abbia preso semplicemente dalla wiki, quindi ho dato credito a vim wiki, quindi non è completamente duplicato poiché la spiegazione è completamente diversa.
Kenorb,

Aggiunge più valore, poiché è una versione molto più breve con una spiegazione corretta e sto anche accreditando le fonti giuste. L'uso dei comandi di shell è molto semplice e conveniente. Se le persone non sono d'accordo, possono semplicemente votare verso il basso, nessun grosso problema.
Kenorb,

6

Nello spirito di VimL funzionale:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))restituisce un elenco di tutte le righe nel buffer. '$'è un argomento speciale per il line()quale indica l'ultima riga nel buffer.
  • reverse(...)inverte l'elenco di input, sul posto. Si dovrebbe usare reverse(copy(...))se l'elenco di input non dovesse essere modificato.
  • setline(1, ...)sostituisce la riga specificata con il secondo argomento. Quando il secondo argomento è un elenco, lo stesso numero di righe della lunghezza dell'elenco viene sostituito con il contenuto dell'elenco.

Se lo desideri, puoi anche definire un comando che accetta un intervallo ( %intero buffer predefinito )

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

1
Mi piace questa risposta. Inoltre non evidenzia cose (se hlsearchabilitate) come il :g/comando delle altre risposte ... Le prestazioni sono forse peggiori, però? Dal momento che getline(1, line('$'))ottiene l'intero buffer in memoria. reverse()sembra essere sul posto, quindi dovrebbe occupare pochissimo ricordo in quanto tale ...
Martin Tournoij,

3

Secondo la documentazione di Vim usr_12.txt - Clever Tricks

12.4 Ordine di riga inversa

Il :globalcomando può essere combinato con il :movecomando per spostare tutte le righe prima della prima, risultando in un file invertito. Il comando è:

:global/^/m 0

abbreviato:

:g/^/m 0

L' ^espressione regolare corrisponde all'inizio della riga (anche se la riga è vuota). Il :movecomando sposta la riga corrispondente dopo la mitica linea zeroth, quindi la riga corrispondente corrente diventa la prima riga del file. Poiché il :globalcomando non viene confuso dalla modifica della numerazione delle righe, :globalprocede a far corrispondere tutte le righe rimanenti del file e inserisce ciascuna come prima.

Questo funziona anche su una gamma di linee. Innanzitutto spostati sopra la prima riga e contrassegnala con mt. Quindi spostare il cursore sull'ultima riga dell'intervallo e digitare:

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

1

Usando i numeri relativi. Il paragrafo inizia alla riga 13 e contiene altre 4 righe

 :13,13+4g/^/m12
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.