Sostituire la seconda occorrenza in linea


12

Ho un elenco di file:

./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 voglio rimuovere il temp.su ogni riga, ma solo la seconda occorrenza , quindi, il file dovrebbe apparire come:

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

Come dovrei farlo?


È possibile che ci sia una terza o quarta occorrenza su qualsiasi linea? Questi devono rimanere intatti?
James,

Il mio caso era quello di abbinare il vecchio nome file al nuovo nome file, quindi solo 2 occorrenze saranno presenti su ogni riga. E solo il secondo dovrebbe cambiare.
nobe4,

Risposte:


10

In generale puoi abbinare l'ennesima occorrenza di qualcosa usando \zse \{N}. C'è un esempio dato a :help \zs.

Nel tuo caso il comando sarebbe:

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

con la bandiera molto magica puoi ridurre la regex:%s/\v(.{-}\zstemp.){2}//
nobe4

8

Questo è molto più semplice con sed:

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

Con Vim hai bisogno di una corrispondenza non avida, e non è facile estendere il metodo per sostituire la 3a, 4a, ecc. Occorrenza temp. Ma può essere fatto se insisti:

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

Grazie per aver dato la soluzione sed, pensi che sia possibile applicarlo su ogni linea?
nobe4,

@ nobe4 Non sono sicuro di cosa tu voglia dire. sedapplica gli script a ciascuna riga di input a sua volta.
Sato Katsura,

sì, ma puoi passare le linee in un programma esterno e recuperare il risultato.
nobe4,

2
Vuoi dire da Vim? Certo, puoi convogliare le linee sedproprio come puoi inoltrarle a qualsiasi altro programma. Fi: :%!sed 's/\.temp\././2'. sedin UNIX-friendly, legge input stdine scrive risultati stdout.
Sato Katsura,

8

Puoi anche farlo con uno sguardo dietro.

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

Se si desidera rimuovere TUTTE le occorrenze dopo la prima, è possibile aggiungere il gflag alla fine.

\(\)\@<=Cercherà qualsiasi motivo tra \(e \)ma non aggiungerà il testo trovato alla corrispondenza.

Vedi :h \@<=per maggiori informazioni.


6

Puoi usare una macro come già suggerito da @Meshpi. Ma questo è un altro modo in cui puoi ottenere la stessa cosa.

02/temp^Mdwx+

0 - Vai all'inizio di una riga

2 / temp - Cerca temp e vai alla seconda occasione

^ M - Questo è solo premendo il tasto Invio

dw - cancella la parola (temp)

x: elimina "."

+ - Vai all'inizio della riga successiva

E poi puoi semplicemente ripeterlo fino alla fine. E ci saranno molti altri modi per fare lo stesso.


6

Questa soluzione è simile a TessellatingHeckler ma si adatta più facilmente a qualsiasi modello debba essere eliminato.

:g/temp\./normal 2ngnd

Ecco come funziona:

  1. :g/temp\./ per ogni riga corrispondente a "temp".
  2. normal eseguire quanto segue in modalità normale
    1. 2n trova la seconda occorrenza del modello
    2. gn selezionalo
    3. d ed eliminalo.

Questo funzionerà per qualunque modello venga dato :g. normalpuò essere abbreviato in norm. gndè equivalente a dgn. Modifica: in effetti 2ngndè equivalente a d2gn.

Vedi il Wiki di Vim Tips per ulteriori esempi del comando globale.


4

In Vim, puoi definire la colonna prima / a / dopo la quale desideri che si verifichi la corrispondenza con :help \%c:

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

Questo eliminerà ogni .tempcolonna trovata dopo la colonna 17 di ogni riga nel buffer corrente.


Nota 1: /gnon è necessario con il campione.

Nota 2: Uso questa utile funzionalità molto spesso con qmv . Consigliato!


Bene, stavo pensando di creare un plugin, ma è bello!
nobe4,

Vim fa già così tante cose ...
Romainl

Certo, ma non ero a conoscenza di questo strumento. E un po 'di scripting è sempre divertente!
nobe4

6
@ nobe4 Se ti interessa questo tipo di cose, vedi anche vidire vipedal pacchetto moreutils .
Sato Katsura,

4

Questo in realtà è piuttosto semplice. Per abbinare la seconda temperatura, consenti qualsiasi cosa seguita da "temp" seguito da qualsiasi cosa, quindi cerca di nuovo temp.

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

I \zsmezzi "Avviare la selezione qui" in modo che la prima parte della partita (tutto prima della seconda temperatura) non viene rimosso. In realtà è una funzionalità estremamente utile di cui ho appena appreso! Senza di esso, il regex dovrebbe apparire così:

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

Se ce ne sono più di due tempsu una riga, la ricetta elimina l'ultima, anziché la seconda. Dai la colpa all'avidità. :)
lcd047

2
Sì, questo è avido e quindi cancella l'ultimo. Tuttavia, ha detto 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

Invece di regex, andrei con un comando che fa:

  • Vai alla fine della linea
  • Cerca indietro temp
  • Elimina 5 caratteri

ed eseguilo su ogni riga:

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

NB. ^[è speciale e rappresenta premendo il tasto Esc; lo digiti con:Ctrl-q <Esc>


Ma un'opzione regex, che non credo sia stata ancora suggerita, è quella di corrispondere temp.seguita da qualsiasi cosa tranne un ., ancorato alla fine della linea.

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

Che corrisponderà temp.txtalla fine di una riga e lo sostituirà con solo il txtbit.


1

Scriverei una macro con il cursore che inizia in alto a sinistra. qq4f.dfpq

Quindi selezionerei il resto delle righe usando Ved eseguirò la macro su quelle righe con:'<,'>norm!@q

Funzionerà solo se il formato del testo rimane lo stesso in relazione al numero di punti prima della seconda temperatura.


5
Per rendere la tua macro più robusta, dovresti usare una ricerca e ninvece 4f.perché se un nome di file ha più punti, la tua macro potrebbe fallire o eliminare elementi che non dovrebbero essere eliminati.
statox

1

Non c'è nulla che ti impedisca di usare due regex invece di una sola, ovvero cambiare la prima occorrenza e quindi scambiare le colonne:

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

Potrebbe non essere molto intelligente, ma lo trovo molto leggibile.


0

Il mio tentativo

:%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
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.