Gantikan kemunculan kedua secara online

12

Saya punya daftar 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

Dan saya ingin menghapus temp.pada setiap baris, tetapi hanya kejadian kedua , dengan demikian, file tersebut akan terlihat seperti:

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

Bagaimana saya harus melakukan ini?

nobe4
sumber
Apakah mungkin bahwa akan ada kejadian ketiga atau keempat di jalur mana pun? Apakah ini harus tetap utuh?
James
Kasus saya adalah mencocokkan nama file lama dengan nama file baru, jadi hanya 2 kemunculan yang akan ada di setiap baris. Dan hanya yang kedua yang harus berubah.
nobe4

Jawaban:

10

Secara umum Anda dapat mencocokkan kemunculan Nth dari sesuatu yang menggunakan \zsdan \{N}. Ada contoh yang diberikan di :help \zs.

Dalam kasus Anda, perintahnya adalah:

:%s/\(.\{-}\zstemp\.\)\{2}//
Antony
sumber
dengan bendera verymagic Anda dapat mengurangi regex:%s/\v(.{-}\zstemp.){2}//
nobe4
8

Ini lebih mudah dilakukan dengan sed:

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

Dengan Vim Anda membutuhkan pencocokan non-serakah, dan tidak mudah untuk memperpanjang metode untuk menggantikan ke-3, ke-4, dll temp. Tapi itu bisa dilakukan jika Anda bersikeras:

:%s/\.temp\..\{-}\.\zstemp\.//
Sato Katsura
sumber
Terima kasih telah memberikan solusi sed, apakah menurut Anda dimungkinkan untuk menerapkan ini pada setiap baris?
nobe4
@ nobe4 Tidak yakin apa yang Anda maksud dengan itu. sedberlaku skrip untuk setiap baris input pada gilirannya.
Sato Katsura
ya, tetapi Anda dapat mengirimkan baris ke program eksternal dan mendapatkan kembali hasilnya.
nobe4
2
Maksud Anda dari Vim? Tentu, Anda dapat menyalurkan saluran pipa sedseperti halnya Anda dapat menyalurkannya ke program lain. Fi: :%!sed 's/\.temp\././2'. seddi UNIX-friendly, ia membaca input dari stdindan menulis hasilnya stdout.
Sato Katsura
8

Anda juga dapat melakukan ini dengan melihat di belakang.

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

Jika Anda ingin menghapus SEMUA kejadian setelah yang pertama, Anda dapat menambahkan gtanda di bagian akhir.

\(\)\@<=Akan mencari pola apa pun di antara \(dan \)tetapi tidak akan menambahkan teks yang ditemukan ke kecocokan.

Lihat :h \@<=untuk info lebih lanjut.

Tumbler41
sumber
6

Anda dapat menggunakan makro seperti yang disarankan @Meshpi. Tetapi, ini adalah cara lain Anda dapat mencapai hal yang sama.

02/temp^Mdwx+

0 - Pergi ke awal garis

2 / temp - Cari temp dan lanjut ke kejadian kedua

^ M - Ini hanya menekan tombol Enter

dw - hapus kata (temp)

x - hapus '.'

+ - Pergi ke awal baris berikutnya

Dan, maka Anda bisa mengulanginya sampai akhir. Dan, akan ada lebih banyak cara untuk melakukan hal yang sama.

Durga Swaroop
sumber
6

Solusi ini mirip dengan TessellatingHeckler tetapi lebih mudah disesuaikan dengan pola apa pun yang harus dihapus.

:g/temp\./normal 2ngnd

Begini cara kerjanya:

  1. :g/temp\./ untuk setiap baris yang cocok dengan "temp."
  2. normal jalankan perintah berikut dalam mode normal
    1. 2n temukan arus kedua polanya
    2. gn pilih itu
    3. d dan hapus.

Ini akan bekerja untuk pola apa pun yang diberikan :g. normaldapat disingkat menjadi norm. gndsetara dengan dgn. Sunting: Faktanya 2ngndsetara dengan d2gn.

Lihat Vim Tips Wiki untuk lebih banyak contoh perintah global.

frangio
sumber
4

Di Vim, Anda dapat menentukan kolom sebelum / di / setelah yang Anda inginkan pencocokan Anda terjadi dengan :help \%c:

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

Ini akan menghilangkan setiap yang .tempditemukan setelah kolom 17 dari setiap baris dalam buffer saat ini.


Catatan 1: /gtidak perlu dengan sampel Anda.

Catatan 2: Saya sering menggunakan fitur praktis itu dengan qmv . Direkomendasikan!

romainl
sumber
Rapi, saya berpikir untuk membuat plugin, tapi ini bagus!
nobe4
Vim sudah melakukan banyak hal ...
romainl
Tentu, tetapi saya tidak mengetahui alat ini. Dan sedikit scripting selalu menyenangkan!
nobe4
6
@ nobe4 Jika Anda peduli tentang hal-hal semacam ini, lihat juga vidirdan vipedari paket moreutils .
Sato Katsura
4

Ini sebenarnya cukup sederhana. Untuk mencocokkan temp kedua, izinkan apa pun yang diikuti oleh "temp" diikuti oleh apa pun, lalu cari temp lagi.

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

The \zsberarti "Start seleksi sini" sehingga bagian pertama dari pertandingan (segala sesuatu sebelum suhu kedua) tidak dihilangkan. Ini sebenarnya fitur yang sangat berguna yang baru saja saya pelajari! Tanpa itu, regex harus terlihat seperti ini:

:%s/\(.*temp.*\)temp\./\1
James
sumber
Jika ada lebih dari dua temppada satu baris resep Anda menghapus yang terakhir, bukan yang kedua. Salahkan keserakahan. :)
lcd047
2
Ya, ini serakah, dan karena itu menghapus yang terakhir. Namun, kata 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

Alih-alih regex, saya akan pergi dengan perintah yang tidak:

  • Lompat ke ujung garis
  • Cari mundur untuk temp
  • Hapus 5 karakter

dan jalankan di setiap baris:

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

NB. ^[khusus dan mewakili penekanan tombol Escape; Anda mengetiknya dengan:Ctrl-q <Esc>


Tetapi opsi regex, yang saya pikir belum disarankan, adalah untuk mencocokkan temp.diikuti oleh apa pun kecuali a ., berlabuh ke akhir baris.

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

Yang akan cocok temp.txtdi akhir baris dan menggantinya dengan hanya txtbit.

TessellatingHeckler
sumber
1

Saya akan menulis makro dengan kursor mulai dari kiri atas. qq4f.dfpq

Lalu saya akan memilih sisa baris menggunakan Vdan menjalankan makro pada baris tersebut dengan:'<,'>norm!@q

Ini hanya akan berfungsi jika format teks tetap sama sehubungan dengan jumlah titik sebelum temp kedua.


sumber
5
Untuk membuat makro Anda lebih kuat, Anda harus menggunakan pencarian dan nbukannya 4f.karena jika nama file memiliki lebih banyak titik, makro Anda bisa gagal atau menghapus hal-hal yang tidak boleh dihapus.
statox
1

Tidak ada yang mencegah Anda menggunakan dua regex alih-alih hanya satu, yaitu mengubah kemunculan pertama dan kemudian menukar kolom:

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

Mungkin tidak terlalu pintar tapi saya merasa sangat mudah dibaca.

grochmal
sumber
0

Usaha saya

:%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
SergioAraujo
sumber