Como reverter a ordem das linhas?


24

Como posso reverter a ordem das linhas para que a primeira linha apareça no final e a última linha apareça primeiro? (Podem ser todas as linhas em um buffer, um intervalo de endereços ou uma seleção de modo visual em linhas.)

Eu gostaria de transformar

rat
ox
tiger
⋮
dog
pig

para dentro

pig
dog
⋮
tiger
ox
rat

sem recorrer a um comando externo como tac.


Alguma sugestão para melhores tags nessa questão?
200_success

11
talvez uma nova tag 'pure-vi' ou similar? Eu já vi várias perguntas que se beneficiariam de uma tag que indicaria o desejo de não ter ferramentas externas envolvidas. Devo perguntar sobre isso no Meta?
John O'M.

11
@Carpetsmoker (e qualquer outra pessoa interessada em seguir isso), a pergunta da tag está agora na meta meta.vi.stackexchange.com/questions/1229/…
John O'M.

Respostas:


29

O poder do global funcionará aqui:

:g/^/exe "normal ddggP"

Ou, mais simplesmente (obrigado @tommcdo)

:g/^/move 0

A primeira corresponderá a todas as linhas e, para cada linha, exclua-a e cole-a na parte superior do arquivo. À medida que se move pelo arquivo, ele reverte o texto.

O segundo corresponde de maneira semelhante a todas as linhas e a move para a parte superior do arquivo.

Nota: Ambos funcionam em todo o arquivo e não se aplicarão corretamente à reversão de um subconjunto das linhas. Veja a resposta de Ingo Karkat para uma solução que funciona dentro de um intervalo.

Descrição:

gcomando global
/^/corresponde a qualquer linha que tenha um início (ou seja, todas as linhas)
exeexecute a seguinte sequência de caracteres
"normalexecutar comandos no modo normal
ddexcluir linha
ggmover para o topo da
Ppasta do arquivo acima da posição atual

move 0 move a linha atual para abaixo da linha 0 (que a coloca na posição 1 ou na primeira linha do arquivo)


6
Em vez do :normalcomando, podemos usar o comando Ex :move 0, que move a linha para o início do buffer.
22615 Tommcdo

11
Também :executeé necessário apenas quando o comando precisa ser construído dinamicamente, por exemplo :execute 'normal' g:user_command.
Tommcdo

@tommcdo good points! Eu tenho o hábito de usá-lo :executeporque, muitas vezes, acabo anexando outros comandos Ex após o existente mais tarde, e é mais conveniente que eu :exejá tenha o que existe do que precisar voltar e inseri-lo mais tarde. Infelizmente, esse hábito vazou para esta resposta, onde não se aplica tanto.
John O'M.

11
Mais explicações sobre meu uso de :execute: como uma string é usada , fornece uma definição clara de onde os comandos do modo normal terminam, mesmo que eu não esteja construindo a string, é mais fácil encontrar aspas equilibradas do que procurar <esc>ou o que quer que seja para encerrar o modo. Novamente, isso é preferência e hábito pessoais. :-)
John O'M.

3
Isso funcionará para um intervalo entre: :9,11g/^/move 8... O último número precisa ser o início do intervalo menos 1 (adaptado da resposta do Ingo).
Martin Tournoij 22/02

13

Essa linha única (para o seu ~/.vimrc) define um :Reversecomando; você também pode usar a :globalpeça diretamente, mas a sintaxe da :move(que iterativamente muda as linhas para antes do início do intervalo, revertendo-a) não é fácil de memorizar:

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

11
Como uma FYI para leitores, os <line1>& <line2>são obrigados a fazer isso funcionar em um intervalo, ou seja: :7,9Reverse(são características de command, não globalou move). Quanto mais simples :command! -bar -range=% Reverse :global/^/m 0também irá funcionar, mas apenas para todo o buffer ...
Martin Tournoij

6

Pure Vim:

:g/^/m0

Explicação:

De acordo com :help multi-repeat, :ge seu primo :vtrabalham de duas maneiras.

A primeira passagem de :gmarca todas as linhas correspondentes {pattern}, enquanto a segunda passagem (aparentemente executada começando no início do arquivo e prosseguindo até o final) executa o [cmd]. O uso acima :gaproveita a ordem em que as linhas são processadas (o que provavelmente é bom, embora provavelmente não seja tecnicamente garantido).

Ele funciona primeiro marcando todas as linhas, movendo a primeira linha marcada para a parte superior do arquivo, movendo a segunda para a parte superior do arquivo (acima da linha movida anteriormente) e depois a terceira linha marcada (novamente acima da linha movida anteriormente) linha) e assim sucessivamente até a última linha do arquivo ser movida para o topo, invertendo efetivamente o arquivo.

Observe que se as :glinhas processadas em qualquer ordem que não sejam de cima para baixo, esse comando não funcionaria.

Fonte: Inverta todas as linhas e Potência de g na vim wikia.

Poucos exemplos usando comandos externos:

  • tac(parte dos GNU coreutils - catinvertida):

    :%!tac                                                                                                                                                                                                                                                              
    
  • tail no BSD / OSX (não compatível com POSIX):

    :%!tail -r
    

    -r A opção -r faz com que a entrada seja exibida na ordem inversa, por linha.

    Verifique: man tarpara mais detalhes.

Para mais ideias, consulte:


2
Não é :g/^/m0o mesmo que :g/^/move 0, qual é a resposta de João?
muru

@ muru Acho que sim, mas este é mais curto (de acordo com o vim wikia) e adicionei explicações diferentes com alguns exemplos adicionais de uso de linhas de comando.
kenorb

Sim, votei por causa de outros comandos (vim postar tactambém). Mas eu suspeito que o voto negativo foi por causa da resposta ser repetida.
muru

Sei que isso tacfoi mencionado pelo OP, mas todas as outras perguntas semelhantes seriam duplicadas disso, então é bom mencioná-lo novamente. John tirou esse cmd do comentário do @tommcdo, eu o peguei inicialmente do DerMike , mas acho que ele tirou da wikia, então dei créditos ao vim wikia, para que não seja completamente duplicado, pois a explicação é completamente diferente.
23415 kenorb

Ele agrega mais valor, pois é uma versão muito mais curta com explicações adequadas e também estou creditando as fontes corretas. O uso de comandos shell é muito simples e conveniente. Se as pessoas não concordam, elas podem simplesmente votar em baixa, não é grande coisa.
Kenorb

6

No espírito do VimL funcional:

:call setline(1, reverse(getline(1, line('$'))))
  • getline(1, line('$'))retorna uma lista de todas as linhas no buffer. '$'é um argumento especial para o line()qual indica a última linha no buffer.
  • reverse(...)inverte a lista de entrada no local. Seria necessário usar reverse(copy(...))se a lista de entrada não deveria ser modificada.
  • setline(1, ...)substitui a linha especificada pelo segundo argumento. Quando o segundo argumento é uma lista, o mesmo número de linhas que o comprimento da lista é substituído pelo conteúdo da lista.

Se desejar, também é possível definir um comando que possui um intervalo ( %buffer inteiro padrão )

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

11
Eu gosto desta resposta. Também não destaca coisas (se hlsearchativadas), como o :g/comando das outras respostas ... O desempenho talvez seja pior, embora? Uma vez que getline(1, line('$'))recebe todo o buffer na memória. reverse()parece ser no local, de modo que deve ter muito pouca memória como tal ...
Martin Tournoij

3

De acordo com a documentação do Vim usr_12.txt - Truques inteligentes

12.4 Ordem da linha reversa

O :globalcomando pode ser combinado com o :movecomando para mover todas as linhas antes da primeira linha, resultando em um arquivo invertido. O comando é:

:global/^/m 0

Abreviado:

:g/^/m 0

A ^expressão regular corresponde ao início da linha (mesmo que a linha esteja em branco). O :movecomando move a linha correspondente para depois da mítica linha zeroth, para que a linha correspondente atual se torne a primeira linha do arquivo. Como o :globalcomando não é confundido pela alteração na numeração das linhas, :globalprossegue para corresponder a todas as linhas restantes do arquivo e coloca cada uma como a primeira.

Isso também funciona em várias linhas. Primeiro mova para acima da primeira linha e marque-a com mt. Em seguida, mova o cursor para a última linha no intervalo e digite:

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

1

Usando números relativos. O parágrafo começa na linha 13 e gera mais 4 linhas

 :13,13+4g/^/m12
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.