Vim: s substitui as primeiras N <g ocorrências em uma linha


13

No vim, às vezes tenho ocasião de substituir as primeiras ocorrências de uma partida em uma linha, mas nem todas as que ggostariam. por exemplo:

a a a a a

para

b b b a a

Eu sei que poderia usar :s/a/b/[enter]:[up][enter]:[up][enter], mas isso é tedioso o suficiente em três repetições, tenho linhas com potencialmente mais de 10 substituições.
Eu tentei:

  • :s/a/b/3g: vim reclamou de caracteres à direita.
  • :s/a/b/3: altera a primeira ocorrência nesta e nas duas linhas a seguir.
  • 3:s/a/b: igual ao anterior.
  • :s/a/b/g3: altera todas as ocorrências nesta e nas próximas duas linhas.
  • :3s/a/b: altera a primeira ocorrência na linha 3.
  • :/a/,3/a/s/a/b: altera a primeira ocorrência em cada linha entre a próxima ae a terceira linha que contém ao arquivo (solicitando a reversão, se necessário).
  • :/a/,/\([^a]*a\)\{3\}/s/a/b/: altera a primeira ocorrência em cada linha entre essa e a seguinte com 3 as (e isso não seria facilmente extensível a uma pesquisa com vários caracteres).

E vários outros padrões de endereçamento, nenhum dos quais funcionou. Devo dizer que aprendi :sbastante sobre o comando tentando encontrar uma resposta para esse problema, mas ainda não o resolvi.

Alguém sabe como fazer isto?

(pontos de bônus por faixa específica, por exemplo, segunda a quarta ocorrências)


2
Tenho certeza de que você não pode fazer isso no vim, mas para torná-lo menos tedioso, você sabe sobre "n" e "." no modo visual? Ou seja, você usa / pattern / para encontrar a coisa a ser alterada, altere-a usando "cw" ou o que seja, e use "n" (apenas n, sem aspas) para encontrar o próximo padrão e pressione "." (apenas ponto, sem aspas) para executar a última edição.
precisa

@ BruceEdiger eu conhecia ne ., embora não tenha pensado em usá-los aqui. Certamente uma melhoria, obrigado.
21711 Kevin

s/a/=something/deve fazer o truque ( :help sub-replace-=). Não sou fluente o suficiente no Vim para escrever somethinglogo de cara .
Gilles 'SO- stop be evil'

Nota: :[up][enter]pode ser substituído por &, o que ainda não é o ideal, mas pelo menos é menos doloroso.
Kowh

Respostas:


10

Com base na :s/pattern/replacement/gc idéia de Samus_ (que parece ser a maneira mais simples de garantir a operação correta quando o padrão está contido na cadeia de substituição), substitua as ocorrências de segunda a quarta em uma única linha:

:call feedkeys("nyyyq") | s/pat/string/gc

feedkeys()é uma função que coloca a sequência de entrada na fila de entrada do teclado. O ponto é fazer a contagem antecipadamente, para que você não precise se preocupar em perder a conta ou ser interrompido.

Para um caso mais geral, substituir as ocorrências Mth a Nth em uma única linha por N maior ou igual a um M muito grande :

:call feedkeys(repeat("n", M-1) . repeat("y", N-M+1) . "q") | s/pat/string/gc

Substitua M e N pelos valores desejados (você pode até deixar vimfazer a aritmética mental trivial se não quiser fazer isso sozinho). Observe que .é o operador de concatenação de strings do VimL. Obviamente, isso só salva as teclas pressionadas para M grandes . Se você usar essa funcionalidade com frequência, poderá economizar tempo para colocar o que foi dito acima em um comando ou função personalizada de algum tipo, pois é bastante digitado.


Hmm. Eu gosto deste. Eu deveria ser capaz de escrever uma função para ela também.
Kevin

8

Para a primeira pergunta, eu faria:

:s/a/b
&&

O segundo é mais complicado, não sei como fazê-lo automaticamente, mas você pode fazer com que o vim solicite a você em cada partida desta forma:

:s/a/b/gc

Em seguida, você responde "não" às primeiras n correspondências e "sim" às outras.


Hmm, esqueci c, essa pode ser a melhor solução proposta ainda. Eu ainda teria que contar, mas acho que é a primeira opção que funcionaria com substituições que contêm a string de pesquisa.
Kevin

6
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a
a a a a a

:3,6g/^/let i=0 | while i<3 | s/a/b/ | let i+=1 | endwhile

a a a a a
a a a a a
b b b a a
b b b a a
b b b a a
b b b a a
a a a a a

1
Isso é bom, mas ainda sofre com o problema apontado por Gilles (em outra postagem, que parece ter sido excluída), que só funciona se a substituição não contiver o padrão. Embora eu goste da ideia de script.
Kevin

Uma coisa de beleza!
bispo

1

Eu acho que isso pode funcionar, substitua primeiro e repita 2 vezes:

:s/a/b/
2@:
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.