Menggunakan Emacs untuk secara rekursif menemukan dan mengganti file teks yang belum terbuka

212

Sebagai tindak lanjut dari pertanyaan ini , ia berusaha mencari tahu bagaimana melakukan sesuatu seperti ini yang seharusnya mudah, yang terutama membuat saya berhenti terbiasa dengan menggunakan Emacs dan bukannya memulai editor yang sudah saya kenal. Saya menggunakan contoh di sini cukup sering dalam mengedit banyak file.

Di Ultraedit saya akan melakukan Alt + s lalu p untuk menampilkan kotak dialog dengan opsi: Cari (termasuk menggunakan ekspresi reguler di beberapa baris), Ganti dengan, Dalam File / Jenis, Direktori, Cocokkan Kasus, Cocokkan Hanya Kata Utuh Saja, Daftar File dan Sub Direktori Pencarian yang Diubah. Biasanya saya pertama-tama akan menggunakan mouse untuk mengklik-seret pilih teks yang ingin saya ganti.

Hanya menggunakan Emacs sendiri (pada Windows XP), tanpa memanggil utilitas eksternal apa pun, cara mengganti semua foo \ nbar dengan bilah \ nbaz di dalam *.cdan *.hfile di beberapa folder dan semua folder di bawahnya. Mungkin Emacs bukan alat terbaik untuk melakukan ini, tetapi bagaimana itu bisa dilakukan dengan mudah dengan perintah minimal?

Rob Kam
sumber

Jawaban:

370
  1. M-x find-name-dired: Anda akan diminta untuk direktori root dan pola nama file.
  2. Tekan tuntuk "toggle mark" untuk semua file yang ditemukan.
  3. Tekan Quntuk "Query-Replace in Files ...": Anda akan dimintai kueri / substitusi regexps.
  4. Lanjutkan seperti dengan query-replace-regexp: SPACEuntuk mengganti dan pindah ke pertandingan berikutnya, nuntuk melewati pertandingan, dll.
  5. Tekan C-x suntuk menyimpan buffer. (Anda kemudian dapat menekan y, natau !untuk menyimpan sekaligus)
Chris Conway
sumber
10
Tidak, ini bersifat rekursif. Versi non-rekursif akan menjadi% m dalam mode dired.
Chris Conway
5
Mungkin karena Anda memanggil C: \ WINDOWS \ system32 \ find.exe, bukan GNU find.
Jazz
6
Steen, Chris: Mungkin bukan apa yang Anda inginkan tetapi Anda dapat menggunakan 'Cx s' (save-some-buffer), itu akan meminta Anda untuk setiap file yang dimodifikasi, jadi Anda hanya perlu menekan 'y' beberapa kali.
danielpoe
48
C-x s !untuk menyimpan semua file sekaligus.
cYrus
10
M-,untuk melanjutkan mencari dan mengganti (misalnya setelah permintaan "kembalikan file" jika file berubah pada disk).
tfischbach
37
  • M-x find-name-dired RET
    • mungkin dibutuhkan beberapa waktu untuk semua file muncul dalam daftar, gulir ke bawah ( M->) hingga find finishedmuncul " " untuk memastikan mereka semua telah memuat
  • Tekan tuntuk "toggle mark" untuk semua file yang ditemukan
  • Tekan Quntuk "Query-Replace in Files ...": Anda akan dimintai kueri / substitusi regexps.
  • Lanjutkan seperti dengan query-replace-regexp: SPACE atau yuntuk mengganti dan pindah ke pertandingan berikutnya, n untuk melewatkan pertandingan, dll.
    • Tipe ! untuk mengganti semua kemunculan dalam file saat ini tanpa diminta, Nuntuk melewati semua kemungkinan penggantian untuk sisa file saat ini. ( Nhanya emacs 23+)
    • Untuk melakukan penggantian pada semua file tanpa bertanya lebih lanjut, ketikkan Y.
  • Panggil "ibuffer" ( C-x C-bjika terikat dengan ibuffer, atau M-x ibuffer RET) untuk mendaftar semua file yang dibuka.
  • Ketik * uuntuk menandai semua file yang belum disimpan, ketik Suntuk menyimpan semua file yang ditandai
  • * * RETuntuk menghapus tanda semua tanda, atau ketik Duntuk menutup semua file yang ditandai

Jawaban ini digabungkan dari jawaban ini , dari situs ini , dan dari catatan saya sendiri. Menggunakan Emacs 23+.

Frank Henard
sumber
3
ibuffertidak terikat C-x C-bsecara default.
phils
2
Nah secara pribadi saya merekomendasikan bahwa orang yang mengikat C-x C-buntuk ibuffer, karena itu jauh lebih baik daripada standar mengikat. Cukup tambahkan (global-set-key (kbd "C-x C-b") 'ibuffer)ke file .emacs Anda. Jika gagal, gunakan M-x ibuffer RETpetunjuk di atas.
phils
baik. Saya pikir saya memiliki kit startup emacs, itulah sebabnya saya terikat
Frank Henard
1
File bla-bla-bladikunjungi hanya-baca dan berhenti untuk mencari. Bagaimana cara menghindari / mengabaikannya dan terus mengganti? Terima kasih.
Felipe
3
Mengapa ibufferketika Anda bisa C-x s( save-some-buffers) dan mengetik !? Saya tidak ingin menjadi kata-kata kasar, hanya ingin tahu. PS plus untuk menggambarkan !, Ndan Y.
d12frosted
17

Jawaban yang diberikan sangat bagus, namun saya pikir saya akan menambahkan pendekatan yang sedikit berbeda.

Ini metode yang lebih interaktif, dan membutuhkan wgrep, rgrepdan iedit. Keduanya ieditdan wgrepharus diinstal melalui MELPA atau Marmalade (menggunakan M-x package-list-packages)

Pertama jalankan M-x rgrepuntuk menemukan string yang Anda cari.

Anda dapat menentukan jenis / pola file dan folder yang akan diulang.

Selanjutnya Anda harus menjalankan wgrepmulai dengan C-s C-p.

Wgrep akan membiarkan Anda mengedit rgrephasilnya, jadi atur suatu wilayah pada string untuk mencocokkan dan mulai iedit-modedengan C-;(tergantung pada terminal Anda, Anda mungkin perlu mengikat kembali ini)

Semua kejadian akan dapat diedit sekaligus. C-x C-suntuk berkomitmen wgrep. Kemudian C-x s !untuk menyimpan file yang diubah.

Manfaat utama dari metode ini adalah Anda dapat menggunakannya iedit-modeuntuk menonaktifkan pertandingan tertentu M-;. Anda juga dapat menggunakan hasil rgrepuntuk melompat ke file, misalnya jika Anda memiliki kecocokan yang tidak terduga.

Saya merasa sangat berguna untuk melakukan pengeditan sumber dan penggantian nama simbol (variabel, nama fungsi dll.) Di seluruh proyek.

Jika Anda belum tahu / menggunakan mode iedit itu alat yang sangat berguna, saya sangat menyarankan Anda melihatnya.

ocodo
sumber
Teknik ini cukup kuat bahkan tanpa menggunakan iedit. Ternyata mengetahui cara melakukan grep (mudah dengan lgrep / rgrep) menjadi tahu cara mengganti pencarian sekaligus!
NeilenMarais
Menggabungkan rgrep / wgrep / macro mengagumkan.
ocodo
2
Pengikat tombol untuk memulai wgrepadalah C-c C-pbtw.
techniao
15

Proyektil sangat bagus: Cc pr menjalankan perintah proyektil-ganti

eflanigan00
sumber
13

Saya biasanya menggunakan alat lain untuk melakukan tugas ini, dan sepertinya banyak pendekatan yang disebutkan di shell entri Find and Replace Across Files dari EmacsWiki , tetapi Paket Findr terlihat sangat menjanjikan.

Mencuri bagian dari file sumber :

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.
Blair Conrad
sumber
bagaimana cara mencoba menemukan string "Collectible" di semua file dengan ekstensi java dan hxx menggunakan findr.el?
swdev
@swdev: Mx findr-query-replace RET Koleksi RET <penggantian> RET. * \. java RET <direktori untuk mulai mencari di> RET
ShreevatsaR
Saya suka pendekatan ini (itu menghindari semua toggling yang terlibat dalam pendekatan langsung). Saya menggunakan beberapa fungsi kenyamanan untuk menggabungkan ini dengan proyektil (project-root-discover), dan mengganti hal tersebut di titik: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ
6

Sumber informasi: 1

Untuk pengguna pro emacs:

  1. Panggilan dired untuk daftar file dalam dir, atau panggilan find-dired jika Anda memerlukan semua subdirektori.
  2. Tandai file yang Anda inginkan. Anda dapat menandai dengan regex dengan mengetikkan 【% m】.
  3. Ketik Q untuk memanggil dired-do-query-replace-regexp.
  4. Ketik regex temukan Anda dan ganti string. 〔☛ pola regex elisp umum〕
  5. Untuk setiap kejadian, ketik y untuk mengganti, n untuk melewati. Ketik 【Ctrl + g】 untuk membatalkan seluruh operasi.
  6. Tipe ! untuk mengganti semua kemunculan dalam file saat ini tanpa diminta, N untuk melewati semua kemungkinan penggantian untuk sisa file saat ini. (N hanya emacs 23)
  7. Untuk melakukan penggantian pada semua file tanpa bertanya lebih lanjut, ketik Y. (khusus Emacs 23)
  8. Panggil ibuffer untuk mendaftar semua file yang dibuka. Ketik 【* u】 untuk menandai semua file yang belum disimpan, ketik S untuk menyimpan semua file yang ditandai, ketik D untuk menutup semuanya.

Panduan Langkah-demi-Langkah untuk Pemula Emacs

Pilih File Target

Mulai emacs dengan mengetikkan "emacs" di prompt antarmuka baris perintah. (Atau, klik dua kali ikon Emacs jika Anda berada di lingkungan Graphics User Interface)

Memilih File dalam Direktori

Pertama, Anda perlu memilih file yang ingin Anda ganti. Gunakan menu grafis 〖File ▸ Open Directory〗. Emacs akan meminta Anda untuk jalur direktori. Ketikkan jalur direktori, lalu tekan Enter.

Sekarang, Anda akan diperlihatkan daftar file, dan sekarang Anda harus menandai file yang Anda ingin regex temukan / ganti untuk bekerja. Anda menandai file dengan memindahkan kursor ke file yang Anda inginkan, lalu tekan m. Hapus tanda dengan menekan u. (Untuk daftar subdirektori, pindahkan kursor ke direktori dan tekan i. Konten sub-direktori akan terdaftar di bagian bawah.) Untuk menandai semua file dengan regex, ketik 【% m】, lalu ketik pola regex Anda. Misalnya, jika Anda ingin menandai semua file HTML, ketikkan 【% m】 lalu .html $. (Anda dapat menemukan daftar perintah tanda di menu grafis "Tandai" (menu ini muncul ketika Anda berada dalam mode dired).)

Memilih File dalam Direktori dan Semua Sub-Direktorinya

Jika Anda ingin mencari / mengganti file di dalam direktori, termasuk ratusan subdirektori, berikut adalah metode untuk memilih semua file ini.

Hubungi find-dired. (Anda memanggil perintah dengan menekan 【Alt + x】) Kemudian, ketikkan nama direktori, ⁖ / Users / mary / myfiles

Catatan: jika Anda menggunakan emacs pada terminal teks non-grafis unix, dan jika 【Alt + x】 tidak berfungsi, stroke key yang setara adalah 【Esc x】.

Emacs akan meminta Anda dengan prompt “Run find (with args):”. Jika Anda perlu melakukan penggantian pada semua file HTML, maka ketik -name "* html". Jika Anda tidak peduli tentang jenis file apa tetapi hanya semua file di bawah dir itu, maka berikan "-type f".

Sekarang, tandai file seperti dijelaskan di atas.

Cari / Ganti Interaktif

Sekarang, Anda siap melakukan penggantian pencarian interaktif. Untuk kesederhanaan, katakanlah Anda hanya ingin mengganti kata "cepat" dengan "super". Sekarang, panggil dired-do-query-replace-regexp. Ini akan meminta Anda untuk string regex dan string pengganti. Ketik "cepat", masukkan, lalu "super".

Sekarang, emacs akan menggunakan pola Anda dan memeriksa file, dan berhenti dan menunjukkan kepada Anda setiap kali terjadi kecocokan. Ketika ini terjadi, emacs akan meminta Anda, dan Anda memiliki pilihan untuk melakukan perubahan atau melewatkan perubahan. Untuk melakukan perubahan, ketik y. Untuk melewati, ketik n. Jika Anda hanya ingin emacs melanjutkan dan membuat semua perubahan pada file saat ini, ketik!

Jika Anda ingin membatalkan seluruh operasi tanpa menyimpan perubahan yang Anda buat, ketik 【Ctrl + g】, lalu keluar emacs menggunakan menu 〖File ▸ Keluar Emacs〗.

Menyimpan File yang Diubah

Sekarang, setelah Anda melewati cobaan di atas, ada satu langkah lagi yang perlu Anda lakukan, dan itu menyimpan file yang diubah.

Jika Anda menggunakan emacs versi 22 atau lebih baru, panggil ibuffer untuk masuk ke mode daftar buffer, lalu ketik 【* u】 untuk menandai semua file yang belum disimpan, kemudian ketik S untuk menyimpan semuanya. (itu shift-s)

Jika Anda menggunakan emacs versi 21, maka Anda dapat melakukan ini: panggil daftar-buffer, kemudian pindahkan kursor ke file yang ingin Anda simpan dan ketik s. Ini akan menandai file untuk tindakan penyimpanan nanti. Ketik u untuk menghapus tanda. Setelah selesai, ketik x untuk menjalankan penyimpanan semua file yang ditandai untuk disimpan. (dalam emacs, file yang dibuka disebut "buffer". Mengabaikan hal-hal lain di sana.)

Alternatif untuk opsi di atas, Anda juga dapat memanggil save-some-buffer 【Ctrl + xs】. Kemudian emacs akan menampilkan setiap file yang belum disimpan dan bertanya apakah Anda ingin menyimpannya.

Catatan: regex emacs tidak sama dengan Perl atau Python, tetapi serupa. Untuk ringkasan dan pola umum, lihat: Emacs Regex.

Sharma Prashant
sumber
2
Saya harap jawaban ini membuatnya lebih ramah pada pemula daripada jawaban yang paling banyak dipilih. Terima kasih atas cara memotivasi saya untuk meletakkan semuanya alih-alih tautannya.
Prashant Sharma
Jawaban dimulai dengan sumber: sebagai baris pertama. Dasar pemikiran untuk menempelkan salinan adalah: kadang-kadang tautan eksternal ke blog menjadi usang dan kemudian jawabannya akan sia-sia. Dan demikian seperti yang disarankan oleh meta wiki. Saya memutuskan untuk menyalin semuanya.
Prashant Sharma
5

Menggunakan dired untuk mengulang pohon direktori yang dalam akan menjadi agak lambat untuk tugas ini. Anda mungkin mempertimbangkan untuk menggunakan tag-query-replace . Ini berarti keluar untuk membuat tabel tag, tetapi itu sering berguna, dan ini cepat.

Alex Coventry
sumber
Baru saja mencoba pada direktori dengan lebih dari 14.000 file. Butuh beberapa detik untuk muncul di Emacs. Jadi pasti tidak lambat (lagi).
Berend de Boer
3

Untuk buffer terbuka, inilah yang saya lakukan:

(defun px-query-replace-in-open-buffers (arg1 arg2)
  "query-replace in all open files"
  (interactive "sRegexp:\nsReplace with:")
  (mapcar
   (lambda (x)
     (find-file x)
     (save-excursion
       (goto-char (point-min))
       (query-replace-regexp arg1 arg2)))
   (delq
    nil
    (mapcar
     (lambda (x)
       (buffer-file-name x))
     (buffer-list)))))
yPhil
sumber
3

M-X Dired, dan t untuk menandai semua file, dan Quntuk meminta ganti teks pada semuanya. Anda dapat memperluas sub direktori dengan menggunakan iperintah sebelum query-replace. Mereka info kunci yang saya tambahkan adalah bahwa jika Anda memberikan awalan (control-u) ke perintah i, itu akan meminta Anda untuk arg, dan argumen -R akan secara rekursif memperluas semua subdirs ke diredbuffer. Jadi sekarang Anda dapat mencari-cari setiap file di seluruh direktori.

Zack Murray
sumber
2

Pilihan lain adalah menggunakan pencarian Icicles . Ini adalah jenis pencarian inkremental yang berbeda yang menggunakan penyelesaian input minibuffer Anda terhadap hit pencarian. Saat Anda memodifikasi input Anda saat ini, set hits yang cocok diperbarui dalam buffer *Completions*.

Anda dapat mencari sejumlah file, buffer, atau lokasi yang ditandai, yang dapat Anda pilih menggunakan pola minibuffer (mis. Regexp).

Ketika Anda mengunjungi hit pencarian, Anda dapat mengganti berdasarkan permintaan baik seluruh hit atau hanya bagian yang sesuai dengan input minibuffer Anda saat ini. Penggantian atas permintaan berarti Anda tidak ditanya tentang setiap klik pencarian pada gilirannya; Anda mengakses hit yang Anda inginkan secara langsung, dalam urutan apa pun. Pendekatan ini bisa lebih efektif daripada penggantian-kueri jika Anda memiliki sejumlah pengganti untuk dilakukan: Anda melewatkan petunjuk lengkap y/n.

Pencarian melebihi konteks pencarian yang Anda tetapkan - Anda tidak terbatas pada pencarian semua teks dalam file target (misalnya, Anda dapat melewatkan komentar atau jenis bagian program tertentu). Contoh sederhana dari konteks pencarian adalah baris, seperti pada grep, tetapi konteks dapat berupa blok teks yang cocok dengan pola yang Anda suka. Biasanya Anda menentukan konteks pencarian menggunakan regexp, tetapi Anda bisa menggunakan fungsi. Selain menentukan sendiri, ada perintah pencarian Icicles yang telah ditentukan untuk berbagai jenis konteks: blok properti teks atau properti overlay, hal-hal pada titik, dll.

Anda juga dapat mengurutkan hit pencarian dalam berbagai urutan sortir untuk akses / navigasi yang lebih mudah.

Drew
sumber
2

find-name-dired tidak apa-apa, tetapi:

  • Semua file yang Anda dapatkan cocok dengan yang sama, satu regexp.
  • find-diredlebih fleksibel dalam hal itu, tetapi juga dibuat untuk menggunakan aturan umum (bahkan jika mereka bisa rumit). Dan tentu saja findmemiliki bahasanya sendiri yang kompleks.
  • jika kemudian Anda ingin bertindak hanya pada beberapa file yang namanya dikumpulkan di find(-name)-diredbuffer, Anda harus menandai atau menghapus / menghilangkan baris yang tidak ingin Anda tindak lanjuti.

Alternatifnya adalah dengan menggunakan perintah Dired + yang bekerja pada (a) file yang ditandai dan (b) semua file yang ditandai (atau semua file, jika tidak ada yang ditandai) di subdirektori yang ditandai ... ditemukan secara rekursif . Ini memberi Anda keumuman dan kontrol yang mudah atas pilihan file. Semua perintah "di sini dan di bawah" ini semuanya berada pada kunci awalan M-+dalam mode Dired.

Misalnya, M-+ Qsama dengan Q--- ganti-permintaan, tetapi file target semuanya ditandai di direktori saat ini dan di setiap subdir yang ditandai, turun, turun, turun ...

Ya, alternatif untuk menggunakan perintah di sini dan di bawah ini adalah dengan memasukkan semua subdir dan subdirnya, secara rekursif, dan kemudian menggunakan perintah tingkat atas seperti Q. Tetapi seringkali lebih mudah untuk tidak repot-repot dengan subdir yang dimasukkan.

Dan untuk melakukan itu Anda tetap memerlukan cara cepat untuk memasukkan semua subdirs seperti itu secara rekursif . Di sini juga, Dired + dapat membantu. M-+ M-imenyisipkan semua subdir yang ditandai dan subdirr yang ditandai sendiri, secara rekursif. Yaitu, seperti M-i(yang menyisipkan subdir yang ditandai di Dired + ), tetapi berfungsi secara rekursif pada subdir.

(Semua perintah Dired + "here-and-below" ada pada menu Multiple > Marked Here and Below .)

Anda juga dapat melakukan operasi dired pada Emacs fileset , yang merupakan satu set disimpan dari nama-nama file yang berada di mana saja. Dan jika Anda menggunakan Icicles maka Anda dapat membuka buffer Dired hanya untuk file dalam fileset atau jenis lain dari daftar file yang disimpan.

Anda juga dapat mem - bookmark buffer Dired apa pun, termasuk yang Anda buat menggunakan find(-name)-dired. Ini memberi Anda cara cepat untuk kembali ke set tersebut (misalnya set proyek) nanti. Dan jika Anda menggunakan Penanda + lalu mem-bookmark catatan Buffer Dired (a) lssakelar, (b) file mana yang ditandai, (c) subdirektori mana yang dimasukkan, dan (d) direktori (sub) mana yang disembunyikan. Semua itu dipulihkan saat Anda "melompat" ke bookmark. Bookmark + juga memungkinkan Anda menandai seluruh pohon buffer Dired --- melompat ke bookmark mengembalikan semua buffer di pohon.

Drew
sumber
Siapa pun: Peduli untuk menjelaskan mengapa Anda menurunkan jawaban ini?
Drew
1

Saya ingin menyarankan satu alat hebat lagi yang belum disebutkan, yaitu Helm .

Ini adalah pengganti yang bagus untuk banyak operasi standar Emacs yang melibatkan penyelesaian, pencarian dll. Secara khusus, helm-find-filesmemungkinkan untuk melakukan penggantian permintaan (termasuk regexp) dalam beberapa file yang dipilih.

Cukup buka helm-find-files, tandai file yang relevan dengan M-SPCdan kemudian gunakan F6atau F7untuk menjalankan permintaan ganti atau permintaan ganti regexp di file yang dipilih.

Andrzej Pronobis
sumber
Bisakah helm-find-filesmenandai dengan regexp, bukan M-SPC?
Nordlow
-2

Ini bukan Emacs, tetapi xxdiff dilengkapi dengan alat bernama xx-rename yang akan melakukan itu untuk beberapa string sekaligus (mis. Dari Dari ke dari KEPADA), dengan dorongan interaktif, simpan cadangan semua file yang dimodifikasi, dan buat pendek log perubahan yang dibuat dengan konteks. Itulah yang cenderung saya gunakan ketika saya melakukan renaming besar / global.

blais
sumber