Como reverter a cada 4 linhas?

13

Antes de tudo, sendo este o meu primeiro post aqui, gostaria de dizer que achei o VIM uma ótima ferramenta e o fórum aqui muito útil para encontrar respostas para perguntas, com muitas pessoas prestando inestimável assistência. Ainda sou muito novo no VIM, então praticamente tudo que aprendi sobre isso veio daqui.

Minha pergunta é: eu sei como reverter TODAS as linhas em um arquivo (: g / ^ / m0 entre outras maneiras), mas existe uma maneira de reverter a cada 4 linhas em um arquivo, para que

line1
line2
line3
line4
line5
line6
line7
line8
...

torna-se

line4
line3
line2
line1
line8
line7
line6
line5
...

Você pode assumir que sempre haverá um múltiplo exato de 4 linhas nesses arquivos.

couldwasiereisawelba
fonte
2
Sobre o seu "ps": se você inserir 4 espaços na frente de uma linha, ele será interpretado como código (você pode selecionar o texto e usar os botões de formatação na parte superior). Você pode encontrar mais detalhes no ícone de interrogação na parte superior .
MMontu

Respostas:

15

O comando :Reverseexplicado no Vim Wiki pode ser usado para isso (você pode incluí-lo no seu .vimrcpara torná-lo permanente):

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

Em seguida, você pode gravar uma macro para executar o comando a cada quatro linhas:

qmV3j:Reverse<cr>4jq
1000@m

Explicação:

  • qm: 'q' no modo normal inicia (e para) a gravação de uma macro em um determinado registro (registrador 'm', neste caso, mas pode ser qualquer outra letra)
  • V: entre no modo visual e selecione a linha atual
  • 3j: expanda a seleção visual para as próximas 3 linhas
  • :Reverse<cr>: execute o comando Reverse nas linhas selecionadas ( <cr>aqui está a entertecla)
  • 4j: vá para as próximas linhas inalteradas
  • q: para de gravar a macro
  • 1000@m: execute a macro gravada no registro 'm' 1000 vezes (você pode aumentar esse número se o arquivo tiver mais de 4000 linhas)

Editar:

Conforme mencionado nos comentários, você pode usar uma macro recursiva em vez de usar uma contagem:

qmV3j:Reverse<cr>4jq
qM@mq
@m
  • qM: se o registro especificado qestiver em maiúsculas, a macro será anexada ao registro (o que também é útil quando você percebe quando perdeu as últimas etapas em uma macro complexa)
  • @m: executar a macro no registro m
  • q: pare de anexar a macro

Apesar de criado como uma macro, você pode criar um comando para isso, se for uma tarefa comum no seu fluxo de trabalho:

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: define uma nova função
  • let reg_m = @m: salva o conteúdo atual do registro m
  • let @m = "<c-r><c-r>m": insira a macro no registro m- observe que <c-r>é a notação vim para Ctrl+ re que você deve digitar isso, não copiar / colar, para que sua linha seja semelhante let @m = 'V3j:Reverse^M4j@m'e conterá um caractere especial (^ M)
  • normal! @m: o comando normal executa seu argumento conforme foi digitado no modo normal, portanto, executa a macro recursiva
  • let @m = reg_m: restaura o conteúdo do registro m, para que você não precise se lembrar de que esse registro está sendo usado nessa função e evite usá-lo

  • command! Reverse4 call Reverse4(): crie um novo comando para esta função

Dependendo das suas necessidades, você pode aprimorá-lo, por exemplo: passe um argumento para o comando e a função para que funcione para qualquer número de linhas, em vez de ser corrigido em grupos de 4 linhas.

mMontu
fonte
Em vez do 1000@qcorte, você também pode usar uma macro recursiva: qmqqm ... @mq@m.
Doorknob
Quando tentei executar o "qmV3j: Reverse <cr> 4jq", ele dizia "E492: Não é um comando do editor". Eu devo estar fazendo algo errado. Estou usando - e só usei - o GVIM, por sinal. Eu deveria ter mencionado isso em primeiro lugar.
precisa saber é o seguinte
Ah deixa pra lá. Eu descobri - não devo digitar ":" antes da parte "qmV3j: Reverse <cr> 4jq". Funcionou! Muito obrigado, mMontu. Se eu puder perguntar - como faço para incluir o comando ": Reverse" no meu .vimrc?
ablewasiereisawelba
3
@ablewasiereisawelba Para tornar o comando disponível permanentemente, basta escrever a linha command! -bar -range=% Reverse <line1>,<line2>g/^/m<line1>-1|nohl em algum lugar do seu vimrc.
Saginaw
@saginaw Oh, eu não sabia que era tão simples assim. Obrigado.
ablewasiereisawelba
9

Como em todas as ações repetitivas , se você puder fazer algo uma vez com operações básicas de edição, poderá fazê-lo facilmente várias vezes gravando uma macro.

(Neste caso, usarei uma macro recursiva, mas você pode gravar uma macro não recursiva e reproduzi-la várias vezes martelando @@ou usando uma contagem.)

ggqqqqqddjjpkddkkPjddpjj@qq@q

Quebrado

  1. gg: Move para o início do arquivo.
  2. qqq: Limpe o registro q. Veremos por que isso é necessário na etapa 5.
  3. qq: Comece a gravar uma macro para registrar q
  4. ddjjpkddkkPjddpjj: Uma série de operações que reordena as quatro primeiras linhas simplesmente excluindo e colando-as manualmente. (Isso pode parecer complicado à primeira vista, mas não se deixe enganar Isso é coisa muito básico que você aprendeu nos primeiros 5 minutos ou mais de. vimtutor: j, k, dd, p, P)
  5. @q: Ligue para a macro! Nesse ponto, o registro qnão contém nada (porque o limpamos na etapa 2), portanto nada acontecerá.
  6. q: Pare de gravar a macro.
  7. @q: Repita a macro recursiva quantas vezes for necessário.

Nota: O acima não produzirá os resultados desejados se o arquivo contiver um número de linhas que não sejam exatamente divisíveis por quatro. Para interromper a macro mais cedo, se não houver mais quatro linhas, precisamos executar um comando no modo normal do Vim que falhará, antes de começarmos a aplicar as edições na etapa 4. Podemos fazer isso tentando mover para baixo uma linha três vezes (e depois faça backup) antes de começarmos a mover as linhas:jjj3k

Portanto:

ggqqqqqjjj3kddjjpkddkkPjddpjj@qq@q
Rico
fonte
Por que você exclui explicitamente o registro q? Se você apenas gravar nele, o que quer que esteja lá dentro será sobrescrito de qualquer maneira ... #
Wildcard
4
@Wildcard Por ser uma macro recursiva, na primeira vez em que você chama o conteúdo do registro q, ela deve estar vazia, caso contrário, estragará suas edições.
Saginaw
Muito agradável. Eu tentei isso de forma interativa, sem tentar digitar a seqüência exata de seu comando, e acabei usando:qqqggqqjjjkddkkPjddjpkddkkPjjjj@qq@q
Curinga
1
@ablewasiereisawelba where I can just use the one-line custom command each time I need to do this- observe que você não precisa recriar a macro toda vez que precisar usá-la, pois é possível armazená-la como uma função / comando no seu vimrc.
MMontu
1
@ablewasiereisawelba Estou feliz que você achou o "Editar" útil! Como esta foi sua primeira postagem aqui, talvez você não saiba como as notificações do StackExchange funcionam: quando você publica um comentário, o autor da resposta / perguntas recebe uma notificação; outras pessoas receberão uma notificação apenas se você incluir o @ <nick>. Portanto, apenas Rich recebeu notificações sobre suas últimas mensagens.
MMontu
7

Você também pode fazer isso com um Excomando usando sedcomo filtro externo:

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

Esta versão ignorará (excluir) quaisquer linhas extras além de um múltiplo de 4. Para manter o último conjunto de menos de 4 linhas (invertido), use:

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

o % aqui significa "Toda linha no buffer".

O !comando significa "Execute o seguinte comando com as linhas especificadas como entrada e substitua as linhas especificadas pela saída do comando". (É chamado de filtro; muito útil para coisas como classificação, por exemplo, :%!sortclassificará todas as linhas do seu arquivo; :2,8!sortclassificará as linhas 2 a 8, etc.)

sedé a ferramenta de edição de fluxo e é encontrada em todos os sistemas semelhantes ao Unix. Os principais conceitos sedusados ​​aqui são o "espaço do padrão" (que por padrão contém apenas cada linha da entrada) e o "espaço de espera" (onde é possível colar texto extra ao usarsed para salvá-lo enquanto processa outras linhas de entrada).

-né uma opção do sedcomando para suprimir suas ações padrão de impressão do espaço do padrão (porque, neste caso, só queremos imprimir quando o explicitarmos).

$pno sedcomando significa "Se você estiver na última linha desed entrada da impressora, imprima (o espaço do padrão)."

h significa "colar o conteúdo atual do 'espaço padrão' no 'espaço de espera', substituindo o que estiver lá".

n significa "substituir o conteúdo do 'espaço padrão' pela próxima linha da entrada".

G significa "anexado ao 'espaço padrão': uma nova linha seguida pelo conteúdo do 'espaço reservado'."

Tomados em conjunto, o sedcomando armazena quatro linhas de saída, revertendo-as à medida que as armazena e depois as imprime. Os $pcomandos adicionados na segunda versão garantem que, se a última linha do arquivo for alcançada, exceto em um múltiplo de 4 linhas, as linhas ainda serão impressas.


Para uma abordagem alternativa e interativa, ainda sem o uso de recursos específicos do Vim e também sem o uso de um filtro externo:

:4

para ir para a quarta linha.

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

para inverter as quatro linhas anteriores (1-4) e deixar o cursor na linha 8.

.m -4move a linha atual para logo após a linha quatro linhas para trás (deixando o cursor na linha movida).

+3m .move a linha que está 3 linhas após a linha atual, para logo após a linha atual, deixando o cursor na linha movida. +2m .claro que funciona da mesma maneira.

+5 coloca o cursor cinco linhas abaixo de onde está.

Repita conforme desejado.

No Vim, você pode repetir todo esse comando com @:e depois repetir com @@. No POSIX viou exvocê precisaria inserir :.m -4 | +3m . | +2m . | +5 como uma linha de texto, exclua-o em um buffer nomeado (registrador) e execute esse buffer nomeado (registrador).

Portanto, no exmodo, revertendo linhas interativamente usando apenas os recursos especificados no POSIX e começando com 17 linhas de texto:

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

Leitura adicional:

Curinga
fonte
Você poderia explicar um pouco mais a sua solução?
vappolinario 29/01
3
@ vappolinario, adicionei mais explicações. Isso ajuda? :)
Curinga
Uau, resposta muito longa. :) Eu sed, na verdade, mas eu apenas o usei com um script simples que encontrei em algum lugar - provavelmente nesta placa - que foi uma solução para um problema que eu tinha. No entanto, quando tentei executar sua linha sugerida, ele dizia "'sed' não é reconhecido como um comando interno ou externo, programa operacional ou arquivo em lotes". Não tenho certeza se preciso ter sed na pasta vim ou o quê.
ablewasiereisawelba
1
@ablewasiereisawelba, se você estiver executando em uma caixa de Windows e não usando Cygwin (ou MobaXterm ou similar), você pode tentar outro método que deu: 4G:.m -4 | +3m . | +2m . | +5<Enter>@:em seguida, @@repetido até que o arquivo é totalmente processada. Eu recomendo instalar o MobaXterm, no entanto. :)
Curinga
@ Wildcard Oh ok, estou vendo o MobaXterm agora. :)
ablewasiereisawelba
7
:g/^/exe 'm .-' . substitute(line('.') % 4, '^0$', '4', '')

Inglês simples: para cada linha, mova a linha atual para cima lnum % 4, a menos que lnum % 4 == 0, nesse caso, mova a linha atual para cima 4.

Além disso, para reverter todas as nlinhas, substitua as 4no comando acima por n.

djjcast
fonte
2
Muito bom comando de uma linha. Obrigado, djjcast.
ablewasiereisawelba