Substitua a segunda ocorrência on-line


12

Eu tenho uma lista de arquivos:

./a.temp.txt     ./a.temp.txt
./a/b.temp.txt   ./a/b.temp.txt
./a/b/c.temp.txt ./a/b/c.temp.txt

E eu quero remover o temp.em cada linha, mas apenas a segunda ocorrência , assim, o arquivo deve se parecer com:

./a.temp.txt     ./a.txt
./a/b.temp.txt   ./a/b.txt
./a/b/c.temp.txt ./a/b/c.txt

Como devo fazer isso?


É possível que ocorra uma terceira ou quarta ocorrência em qualquer linha? Eles precisam permanecer intactos?
James

Meu caso foi combinar o nome do arquivo antigo com o novo nome do arquivo, portanto, apenas 2 ocorrências estarão em cada linha. E apenas o segundo deve mudar.
nobe4

Respostas:


10

Em geral, você pode corresponder à enésima ocorrência de algo usando \zse \{N}. Há um exemplo dado em :help \zs.

No seu caso, o comando seria:

:%s/\(.\{-}\zstemp\.\)\{2}//

com a bandeira verymagic você pode reduzir o regex:%s/\v(.{-}\zstemp.){2}//
nobe4

8

Isso é muito mais fácil com sed:

sed 's/\.temp\././2'

Com o Vim, você precisa de uma correspondência não gananciosa, e não é fácil estender o método para substituir a 3ª, 4ª, etc. ocorrência de temp. Mas isso pode ser feito se você insistir:

:%s/\.temp\..\{-}\.\zstemp\.//

Obrigado por fornecer a solução sed, você acha que é possível aplicar isso em cada linha?
nobe4

@ nobe4 Não sei o que você quer dizer com isso. sedaplica scripts a cada linha de entrada por vez.
Sato Katsura 07/07

sim, mas você pode passar linhas para um programa externo e recuperar o resultado.
nobe4

2
Você quer dizer com Vim? Claro, você pode canalizar linhas sedda mesma maneira que pode canalizá-las para qualquer outro programa. Fi: :%!sed 's/\.temp\././2'. sedem UNIX-friendly, lê entrada de stdine grava resultados para stdout.
Sato Katsura

8

Você também pode fazer isso com um olhar para trás.

:%s/\(temp\..*\)\@<=temp\.//

Se você deseja remover TODAS as ocorrências após a primeira, você pode anexar a gsinalização no final.

\(\)\@<=Irá procurar por qualquer padrão entre \(e \)mas não adicionará o texto encontrado à correspondência.

Veja :h \@<=para mais informações.


6

Você pode usar uma macro como o @Meshpi já sugerido. Mas, essa é outra maneira de realizar a mesma coisa.

02/temp^Mdwx+

0 - Ir para o início de uma linha

2 / temp - Pesquise temp e vá para a segunda ocorrência

^ M - Isso é apenas pressionar a tecla Enter

dw - exclua a palavra (temp)

x - exclua o '.'

+ - Ir para o início da próxima linha

E então você pode repetir até o fim. E haverá muito mais maneiras de fazer o mesmo.


6

Essa solução é semelhante à do TessellatingHeckler, mas é mais facilmente adaptada a qualquer padrão que precise ser excluído.

:g/temp\./normal 2ngnd

Veja como funciona:

  1. :g/temp\./ para cada linha correspondente a "temp".
  2. normal execute o seguinte no modo normal
    1. 2n encontre a segunda ocorrência do padrão
    2. gn selecione
    3. d e exclua-o.

Isso funcionará para qualquer padrão dado :g. normalpode ser abreviado para norm. gndé equivalente a dgn. Edit: Na verdade, 2ngndé equivalente a d2gn.

Veja o Vim Tips Wiki para mais exemplos do comando global.


4

No Vim, você pode definir a coluna antes / depois / após a qual deseja que sua correspondência ocorra :help \%c:

:%s/\%>17c\.temp/g

Isso eliminará todos os itens .tempencontrados após a coluna 17 de todas as linhas no buffer atual.


Nota 1: /gnão é necessário com a sua amostra.

Nota 2: Eu uso esse recurso útil muitas vezes com o qmv . Recomendado!


Legal, eu estava pensando em fazer um plugin, mas isso é legal!
#

Vim já faz tantas coisas ...
romainl 07/07

Claro, mas eu não conhecia essa ferramenta. E um pouco de script é sempre divertido!
nobe4

6
@ nobe4 Se você se preocupa com esse tipo de coisa, veja também vidire vipeno pacote moreutils .
Sato Katsura

4

Isso é realmente muito simples. Para coincidir com a segunda temperatura, permita qualquer coisa seguida por "temp" seguida por qualquer coisa e procure temp novamente.

%s/.*temp.*\zstemp\.

Os \zsmeios "Iniciar a seleção aqui" para que a primeira parte da partida (tudo antes da segunda temperatura) não sejam removidos. Na verdade, é um recurso extremamente útil que acabei de aprender! Sem ele, o regex teria que ficar assim:

:%s/\(.*temp.*\)temp\./\1

Se houver mais de duas tempem uma linha, sua receita excluirá a última, e não a segunda. Culpe a ganância. :)
lcd047

2
Sim, isso é ganancioso e, portanto, exclui o último. No entanto, disse OP My case was to match old filename to new filename, so only 2 occurrences will be on each line. And only the second one should change.
James

2

Em vez de regex, eu iria com um comando que faz:

  • Ir para o final da linha
  • Pesquise para trás temp
  • Excluir 5 caracteres

e execute-o em todas as linhas:

:g//normal $?temp^[5x

NB ^[é especial e representa pressionar a tecla Escape; você digita com:Ctrl-q <Esc>


Mas uma opção regex, que eu acho que não foi sugerida ainda, é corresponder temp.seguida por qualquer coisa, exceto a ., ancorada no final da linha.

:%s/temp\.\([^.]\+\)$/\1/

Que corresponderá temp.txtao final de uma linha e a substituirá por apenas um txtpouco.


1

Eu escreveria uma macro com o cursor começando no canto superior esquerdo. qq4f.dfpq

Depois, selecionava o restante das linhas usando Ve executava a macro nessas linhas com:'<,'>norm!@q

Isso só funcionará se o formato do texto permanecer o mesmo em relação ao número de pontos antes da segunda temperatura.


5
Para tornar sua macro mais robusta, você deve usar uma pesquisa e, em nvez disso, 4f.se um nome de arquivo tiver mais pontos, sua macro poderá falhar ou excluir itens que não devem ser excluídos.
Statox

1

Não há nada que impeça o uso de duas expressões regulares em vez de apenas uma, ou seja, altere a primeira ocorrência e depois troque as colunas:

:%s/\.temp//|%s/^\(\S\+\)\(\s\+\)\(\S\+\)/\3\2\1/

Pode não ser muito inteligente, mas acho muito legível.


0

Minha tentativa

:%norm 4ftdwx

:%norm ......... execute in normal mode over the whole file  
4ft ............ the 4th t matches the start of the second temp
dw ............. deletes the current word
x .............. deletes the dot
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.