Cari dan ganti di Vim di semua file proyek


117

Saya mencari cara terbaik untuk melakukan pencarian dan penggantian (dengan konfirmasi) di semua file proyek di Vim. Yang saya maksud dengan "file proyek" adalah file di direktori saat ini, beberapa di antaranya tidak harus dibuka.

Salah satu cara untuk melakukannya adalah dengan membuka semua file di direktori saat ini:

:args ./**

lalu lakukan pencarian dan ganti pada semua file yang terbuka:

:argdo %s/Search/Replace/gce

Namun, ketika saya melakukan ini, penggunaan memori Vim melonjak dari beberapa lusin MB menjadi lebih dari 2 GB, yang tidak berfungsi untuk saya.

Saya juga menginstal plugin EasyGrep , tetapi hampir tidak pernah berfungsi — baik plugin tidak menemukan semua kemunculannya, atau hanya hang sampai saya menekan CtrlC. Sejauh ini cara yang saya sukai untuk menyelesaikan tugas ini adalah ack-grep untuk istilah pencarian, dengan menggunakan jendela perbaikan cepat buka file apa pun yang berisi istilah dan tidak dibuka sebelumnya, dan akhirnya :bufdo %s/Search/Replace/gce.

Saya sedang mencari plugin yang bagus dan berfungsi yang dapat digunakan untuk ini, atau alternatif perintah / urutan perintah yang akan lebih mudah daripada yang saya gunakan sekarang.


1
@Cascabel Karena Anda menulis komentar ini, ada sebuah situs vi.stackexchange.com.
Flimm

Jawaban:


27

Greplace bekerja dengan baik untukku.

Ada juga versi siap patogen di github.


8
Menginstalnya, saya melihat perintah lain suka :Gsearchdan :Gbuffersearchada, tetapi ketika saya mengetik :Greplacesaya mendapatkan Not an editor command: Greplace.
Ramon Tayag

menurut dokumen, sintaksnya adalah: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] jadi Anda bisa melakukan pencarian rekursif menggunakan, misalnya::Gsearch -r pattern dir/
vitorbal

Hal yang saya benci tentang Greplace adalah jika polanya ada di nama file, Greplace gagal, karena Anda akhirnya menggantinya di nama file juga.
Daniel

2
Tidak ada yang tidak biasa di sini setelah 6 tahun, tetapi plugin ini sangat usang dibandingkan dengan beberapa jawaban baru ( github.com/yegappan/greplace ) pembaruan terakhir, 3+ tahun yang lalu. Saya mungkin memeriksa solusi bawaan oleh Sid: stackoverflow.com/a/38004355/2224331 (Bukan saya di akun lain, hanya kebetulan: P)
SidOfc

99

Opsi besar lainnya di sini adalah tidak menggunakan vim:

sed -i 's/pattern/replacement/' <files>

atau jika Anda memiliki beberapa cara untuk membuat daftar file, mungkin sesuatu seperti ini:

find . -name *.cpp | xargs sed -i 's/pattern/replacement/'
grep -rl 'pattern1' | xargs sed -i 's/pattern2/replacement/'

dan seterusnya!


Terima kasih! Saya benar-benar perlu mempelajari hal-hal baris perintah sekolah tua ini. Saya rasa ini adalah jawaban terbaik. Apakah ini regexes perl regexes atau vim regexes atau sejenis regex lainnya?
Eric Johnson

2
@EricJohnson: Mereka ... sedregex, yang pada dasarnya adalah ekspresi reguler dasar POSIX.2, tetapi tidak persis (ini ada di halaman manual) - mereka membiarkan \nbaris baru cocok, dan beberapa hal serupa lainnya. Oleh karena itu, mereka hampir sama dengan grepekspresi reguler.
Cascabel

Apakah ada alasan Anda memiliki -i dalam perintah sed? Halaman manual mengatakan itu untuk menentukan ekstensi, tetapi Anda tampaknya tidak benar-benar menentukannya.
davekaro

Ok sebenarnya sepertinya 's / pattern / replacement /' adalah ekstensinya, saya rasa saya mengerti sekarang.
davekaro

11
Di Mac OS X, satu-satunya perintah sed yang berhasil untuk saya adalah:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss

74

EDIT: Gunakan cfdoperintah alih-alih cdountuk secara signifikan mengurangi jumlah perintah yang akan dijalankan untuk mencapai ini (karena cdomenjalankan perintah pada setiap elemen saat cfdomenjalankan perintah pada setiap file)

Berkat cdoperintah yang baru saja ditambahkan , Anda sekarang dapat melakukan ini dalam dua perintah sederhana menggunakan grepalat apa pun yang telah Anda instal. Tidak diperlukan plugin tambahan !:

1. :grep <search term>
2. :cdo %s/<search term>/<replace term>/gc
3. (If you want to save the changes in all files) :cdo update

( cdomenjalankan perintah yang diberikan ke setiap istilah dalam daftar quickfix, yang grepdiisi oleh perintah Anda .)

(Hapus cdi akhir perintah ke-2 jika Anda ingin mengganti setiap istilah pencarian tanpa mengonfirmasi setiap kali)


12
Ditambah Anda dapat menambahkan :cdo updateuntuk menyimpan perubahan di semua file.
Zhe Li

1
Tapi itu akan berhenti sejenak untuk memperingatkan tidak disimpan setiap kali sebelum beralih ke buffer berikutnya ketika buffer saat ini diubah.
Bebas

1
@Zhe terima kasih, saya telah menambahkan itu ke jawaban; @lfree Saya pribadi lebih suka mengonfirmasi setiap perubahan, tetapi Anda dapat menghapus cdi akhir perintah kedua (cukup gunakan g) untuk membuatnya mencari dan mengganti tanpa meminta konfirmasi
Sid

1
: cdo% s / istilah pencarian / ganti istilah / g hanya menggantikan kemunculan pertama, tetapi tidak berfungsi untuk kemunculan kedua di vim saya
sunxd

3
untuk menggunakan update, saya harus:cdo %s/<search term>/<replace term>/g | update
gabe

34

Saya telah memutuskan untuk menggunakan ack dan Perl untuk memecahkan masalah ini untuk memanfaatkan ekspresi reguler Perl yang lebih kuat daripada subset GNU.

ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'

Penjelasan

ack

Jack adalah alat baris perintah mengagumkan yang merupakan campuran grep, finddan penuh Perl ekspresi reguler (bukan hanya GNU bagian). Ini ditulis dalam Perl murni, cepat, memiliki penyorotan sintaks, bekerja pada Windows dan lebih ramah bagi pemrogram daripada alat baris perintah tradisional. Instal di Ubuntu dengan sudo apt-get install ack-grep.

xargs

Xargs adalah alat baris perintah unix lama. Itu membaca item dari input standar dan menjalankan perintah yang ditentukan diikuti dengan item yang dibaca untuk input standar. Jadi pada dasarnya daftar file yang dihasilkan oleh ack akan ditambahkan ke akhir perl -pi -E 's/pattern/replacemnt/g'perintah.

perl -pi

Perl adalah bahasa pemrograman. Opsi -p menyebabkan Perl membuat loop di sekitar program Anda yang mengiterasi argumen nama file. Opsi -i menyebabkan Perl mengedit file di tempatnya. Anda dapat memodifikasi ini untuk membuat cadangan. Opsi -E menyebabkan Perl untuk mengeksekusi satu baris kode yang ditentukan sebagai program. Dalam kasus kami, program ini hanyalah substitusi regex Perl. Untuk informasi lebih lanjut tentang opsi baris perintah Perl perldoc perlrun. Untuk informasi lebih lanjut tentang Perl lihat http://www.perl.org/ .


Saya suka jawaban ini karena berfungsi bahkan dengan akhir baris cygwin. Perhatikan bahwa itu menambahkan \ r ke akhir baris yang dimodifikasi, tetapi ketika menggunakan sed, itu akan menggantikan setiap baris baru, membuat diff Anda tidak dapat digunakan. Perl sedikit lebih waras, dan ini adalah satu baris yang sangat bagus untuk pencarian dan penggantian. Terima kasih!
Andrew

@ Andrew, saya tidak yakin apa yang salah dalam kasus Anda. Akankah membantu Anda menggunakan \ R daripada \ n di regexps Anda? Lihat bagian \ R di perldoc.perl.org/perlrebackslash.html#Misc . Coba juga perldoc.perl.org/perlre.html#Regular-Expressions . Perl sangat lintas platform dan saya yakin ada cara bagi Anda untuk tidak berakhir dengan akhiran baris yang rusak.
Eric Johnson

Regex saya tidak memiliki baris baru di dalamnya, versi cygwin dari program ini secara otomatis ditambahkan di akhir setiap baris yang dimodifikasi. Saya secara khusus menyukai versi perl karena hanya memodifikasi baris yang cocok sedangkan sed akan memodifikasi semua baris, bahkan jika tidak cocok dengan regex.
Andrew

Bagaimana jika jalur file berisi spasi?
shengy

23

mungkin melakukan ini:

:noautocmd vim /Search/ **/*
:set hidden
:cfirst
qa
:%s//Replace/gce
:cnf
q
1000@a
:wa

Penjelasan:

  • :noautocmd vim /Search/ **/*⇒ lookup ( vimadalah singkatan dari vimgrep) pola di semua file di semua subdirektori cwd tanpa memicu autocmds ( :noautocmd), demi kecepatan.
  • :set hidden ⇒ biarkan buffer yang dimodifikasi tidak ditampilkan di jendela (bisa di vimrc Anda)
  • :cfirst ⇒ lompat ke hasil pencarian pertama
  • qa ⇒ mulai merekam makro ke register a
  • :%s//Replace/gce⇒ ganti semua kemunculan pola pencarian terakhir (masih /Search/pada saat itu) dengan Replace:
    • beberapa kali pada baris yang sama ( gbendera)
    • dengan konfirmasi pengguna ( cbendera)
    • tanpa kesalahan jika tidak ada pola yang ditemukan ( ebendera)
  • :cnf⇒ lompat ke file berikutnya dalam daftar yang dibuat oleh vimperintah
  • q ⇒ berhenti merekam makro
  • 1000@a ⇒ bermain makro disimpan dalam daftar 1000 kali
  • :wa ⇒ simpan semua buffer yang dimodifikasi

* EDIT * Vim 8 cara:

Dimulai dengan Vim 8, ada cara yang lebih baik untuk melakukannya, karena :cfdomengulangi semua file di daftar quickfix:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa

Anda mungkin ingin menjelaskannya sedikit lebih jauh untuk kita yang lebih rendah. Tidak jelas bagi saya apakah Anda perlu melakukan "urutan" ini setiap saat. Saya lebih cepat melakukan itu menggunakan Delphi 5 lama saya yang berkarat jadi pasti saya kehilangan sesuatu.
Lieven Keersmaekers

1
@Lieven: Anda benar, ini agak tidak jelas tanpa komentar. Saya akan mengembangkan beberapa penjelasan.
Benoit

+1 tetapi meskipun vimtelah memenangkan saya untuk banyak hal, saya pikir saya akan tetap menggunakan PowerGrep dengan ini.
Lieven Keersmaekers

Terima kasih atas jawaban Anda dan penjelasan dari semua perintah, saya sangat menghargainya. Saya menerima jawaban lain karena saya lebih suka menggunakan plugin untuk ini.
psyho

Saya telah menandai ini karena ini menyebabkan lebih banyak kebingungan, mengingat perbedaan antara level seseorang yang mengajukan pertanyaan dan level yang diperlukan untuk memahami jawaban ini.
Lloyd Moore

22

Mengisi :argsdari perintah shell

Dimungkinkan (pada beberapa sistem operasi 1 )) untuk menyediakan file :argsmelalui perintah shell.

Misalnya, jika Anda menginstal ack 2 ,

:args `ack -l pattern`

akan meminta ack untuk mengembalikan daftar file yang berisi 'pola' dan meletakkannya di daftar argumen.

Atau dengan ol 'grep biasa saya kira itu akan menjadi:

:args `grep -lr pattern .`  


Anda kemudian dapat menggunakan :argdoseperti yang dijelaskan oleh OP:

:argdo %s/pattern/replacement/gce


Isi :argsdari daftar quickfix

Juga periksa jawaban nelstrom untuk pertanyaan terkait yang menjelaskan perintah sederhana yang ditentukan pengguna yang mengisi arglist dari daftar quickfix saat ini. Ini berfungsi dengan baik dengan banyak perintah dan plugin yang outputnya berakhir di daftar quickfix ( :vimgrep, :Ack3 , :Ggrep4 ).

Urutan untuk melakukan pencarian luas proyek kemudian dapat dilakukan dengan:

:vimgrep /pattern/ **/*
:Qargs 
:argdo %s/findme/replacement/gc

di mana :Qargspanggilan ke perintah yang ditentukan pengguna yang mengisi arglist dari daftar quickfix.

Anda juga akan menemukan tautan dalam diskusi berikutnya ke plugin sederhana yang membuat alur kerja ini turun menjadi 2 atau 3 perintah.

Tautan

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. buronan - github.com/tpope/vim-fugitive

1
argdo berhenti setelah file pertama dengan masalah tulis
frankster


5

Jika Anda tidak keberatan memperkenalkan ketergantungan eksternal, saya telah membuat plugin ctrlsf.vim (tergantung pada ack atau ag ) untuk melakukan pekerjaan itu.

Itu dapat memformat dan menampilkan hasil pencarian dari ack / ag, dan menyinkronkan perubahan Anda dalam buffer hasil ke file aktual pada disk.

Mungkin demo berikut menjelaskan lebih lanjut

ctrlsf_edit_demo


3
1. :grep <search term> (or whatever you use to populate the quickfix window)
2. :cfdo %s/<search term>/<replace term>/g | update

Langkah 1 mengisi daftar quickfix dengan item yang Anda inginkan. Dalam hal ini, ini disebarkan dengan istilah penelusuran yang ingin Anda ubah grep.

cfdomenjalankan perintah berikut pada setiap file di daftar quickfix. Ketik :help cfdountuk detailnya.

s/<search term>/<replace term>/gmenggantikan setiap istilah. /gberarti mengganti setiap kejadian dalam file.

| update menyimpan file setelah setiap penggantian.

Saya mengumpulkan ini berdasarkan jawaban ini dan komentarnya, tetapi merasa itu pantas mendapatkan jawabannya sendiri karena semuanya ada di satu tempat.


Bagi saya di NeoVim, sedikit modifikasi perlu dilakukan pada baris 2 di atas: 2. :cfdo %s/<search term>/<replace term>/g | updateTanpa %, hanya pola kemunculan pertama yang diganti.
Kareem Ergawy

Terima kasih Kareem. Saya memperbarui jawabannya karena %sbekerja pada vim biasa juga.
Kyle Heironimus

0

Pada dasarnya, saya ingin mengganti dalam satu perintah dan yang lebih penting dalam vim itu sendiri

Berdasarkan jawaban oleh @Jefromi saya telah membuat pintasan keyboard, yang telah saya atur di file .vimrc saya seperti ini

nmap <leader>r :!grep -r -l  * \| xargs sed -i -e 's///g'

sekarang dari vim, dengan stroke pemimpin + r saya mendapatkan perintah dimuat di vim, yang saya edit seperti di bawah ini,

:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'

Oleh karena itu, saya melakukan penggantian dengan satu perintah dan yang lebih penting dalam vim itu sendiri


dan saya benar-benar menggunakan ag daripada grep
deepak
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.