Bagaimana cara mencari / grep konten file secara rekursif dalam direktori / subdirektori di Emacs?

14

Saya menggunakan Emacs untuk beberapa waktu dan saya masih gagal memahami apa cara terbaik untuk (secara rekursif) grep untuk string dalam direktori (proyek) dan mendapatkan hasil yang disajikan baik sebagai buffer dired atau dalam beberapa bahkan lebih cara yang bermanfaat. Tolong beri tahu saya.

Boris Stitnicky
sumber
Sudahkah Anda mencoba menggunakan helm-projectile-grepperintah (jika Anda memiliki proyektil helm terpasang) atau projectile-grep?
Chakravarthy Raghunandan
Saya tidak tahu apa helmatau apa projectile, tetapi jika ini adalah alat yang direkomendasikan, saya akan menginstal dan mempelajarinya.
Boris Stitnicky
Ya, proyektil adalah paket yang menyediakan cara mudah untuk melakukan apa yang Anda sebutkan dalam pertanyaan dengan perintah projectile-grep. Juga, saya SANGAT menyarankan Anda menginstal helmpaket. Saya tidak bisa membayangkan menjalankan emacs tanpa helmdan helm-projectile. Anda mungkin juga tertarik pada helm-agpaket (yang menyediakan antarmuka helm untuk pencari perak di emacs, yang seharusnya lebih cepat daripada grep atau ack).
Chakravarthy Raghunandan
2
Agar pertanyaan lebih bermanfaat dan lebih mudah ditemukan untuk pencarian Google dan forum di masa depan, mungkin pertimbangkan untuk memodifikasi judul pertanyaan untuk memasukkan kata secara rekursif , dan pertimbangkan untuk membatasi ruang lingkup - misalnya, Bagaimana cara secara rekursif mengambil konten file dalam direktori / subdirektori . Itu hanya contoh - itu bisa menjadi sesuatu yang lain.
hukum
1
Sementara komentar dan jawaban sejauh ini telah menyarankan sejumlah paket untuk melakukan ini dengan berbagai cara mewah, saya tidak melihat dari pertanyaan Anda mengapa sederhana M-x greptidak melakukan apa yang Anda butuhkan. Mari kita beri baris perintah sewenang-wenang, saya kadang-kadang menggunakannya di findsana ketika saya membutuhkan rekursi.
PETA

Jawaban:

15

Nah, karena pertanyaan awal tidak disebutkan rgrep, saya akan lanjutkan dan tunjukkan di sini. Ini adalah opsi paling sederhana yang sudah ada di Emacs, dan saya merasa sudah lebih dari cukup untuk sebagian besar hal.

Dari dokumentasi,

Recursively grep for REGEXP in FILES in directory tree rooted at DIR.

Pencarian terbatas pada nama file yang sesuai dengan pola shell FILES.

Jadi, misalnya, untuk mencari semua file python di bawah direktori home saya untuk penggunaan some_function, saya akan menyebutnya dengan some_function, *.py, ~/sebagai argumen. Hasil ditampilkan dalam buffer baru.

pengguna2699
sumber
1
Mungkin ingin disebutkan find-grep-diredjuga karena OP menyebutkan "hasil yang disajikan sebagai buffer dired atau ..."
npostavs
@npostavs Saya belum pernah menggunakan find-grep-direddiriku, silakan menambahkannya ke jawabannya.
user2699
Saya pernah bereksperimen dengan orang-orang seperti ack dan ag, dengan asumsi bahwa saya harus beralih dari rgrep ke sesuatu menggunakan itu, karena mereka seharusnya lebih baik. Pada akhirnya saya tetap dengan rgrep, karena saya tidak menemukan manfaat untuk berubah! Pada baris perintah tentu ada kasus yang harus dibuat, tetapi dalam Emacs rgrepmelakukan pekerjaan yang baik menargetkan pencarian sehingga tidak ada perbedaan kecepatan yang signifikan di antara mereka. (Saya ingin mengatakan bahwa rgrep fraksional lebih cepat dalam beberapa kasus, tapi saya tidak ingat pasti.)
phils
1
Catat itu next-errordan previous-errordapat digunakan untuk menavigasi melalui set hasil. Saya menemukan M-g M-ndan M-g M-pyang paling nyaman dari binding standar.
phils
find-grep-diredtidak memberi saya buffer dengan tautan referensi silang. rgrepmelakukannya untuk saya dan saran terakhir tentang next-error previous-erroritu fantastis! Satu-satunya masalah saya adalah bahwa semua output eksekusi perintah berantakan pada awal buffer.
robert
11

Saya senang menggunakannya M-x find-grep-diredselama bertahun-tahun. Ini mengembalikan hasil sebagai buffer dired, yang bisa Anda gunakan.

Michael Albinus
sumber
Saya mengalami beberapa kesulitan dengan mendapatkan tautan silang yang menghubungkan untuk bekerja dengan ini. Konfigurasi ini cukup buruk ( find-ls-option) dan setelah semua itu Anda berakhir dengan sesuatu yang sangat bertele-tele karena tidak berfungsi tanpa memiliki tanggal sebelum nama file!
robert
5

Solusi 1 (solusi terbaik):

Pasang nasihat ( https://github.com/abo-abo/swiper/blob/master/counsel.el )

Lalu M-x counsel-git-grep.

Tidak diperlukan pengaturan (git tahu root proyek dan file yang dikecualikan). Keduanya git grepdan counselefisien.

Proyek perlu dikelola oleh git.

nasihat membutuhkan ivy-mode.

Solusi 2:

Solusi ini menggunakan grep dan bekerja pada proyek apa pun. Ini lebih rendah daripada Solusi 1 karena lebih lambat dan memerlukan pengaturan manual. Ini juga didasarkan pada ivy-mode.

(defvar simple-project-root "~/.emacs.d")
(defun simple-grep ()
  (interactive)
  (unless (featurep 'ivy)
    (require 'ivy))
  (let* ((default-directory (file-name-as-directory simple-project-root))
         (keyword (read-string "Keyword:")))
    (ivy-read (format "Grep at %s:" simple-project-root)
              (split-string (shell-command-to-string (format "grep -rsnI %s ." keyword)) "\n" t)
              :action (lambda (val)
                        (let* ((lst (split-string val ":"))
                               (linenum (string-to-number (cadr lst))))
                          ;; open file
                          (find-file (car lst))
                          ;; goto line if line number exists
                          (when (and linenum (> linenum 0))
                            (goto-char (point-min))
                            (forward-line (1- linenum))))))))

Anda perlu membuat .dir-locals.el untuk pengaturan simple-project-root, lihat https://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.html untuk detail teknis

Kode dalam solusi 2 hanyalah sebuah prototipe. Implementasi saya yang sebenarnya jauh lebih kompleks. Lihat counsel-etags-grepdi https://github.com/redguardtoo/counsel-etags/blob/master/counsel-etags.el

Ringkasan:

Itu adalah dua solusi terbaik yang saya tahu.

Jika ada solusi lain yang lebih baik, mereka setidaknya perlu menyelesaikan masalah di bawah ini agar siap produksi,

  • cara mendapatkan kata kunci untuk dipahami (misalnya, dapatkan kata kunci dari wilayah yang dipilih)

  • lepas dari kata kunci

  • jika ada program grep yang lebih efisien, kita harus menggunakannya (ripgrep, the_silver_searcher / ag, ... dll), atau mundur grep default

  • jendela kandidat harus menggunakan layar lebar penuh dan pengguna dapat menyaring kandidat secara interaktif (itu sebabnya orang menggunakan ivy atau helm)

  • kita harus menunjukkan jalur relatif di jendela kandidat

  • dapat menggunakan kembali hasil grepped sebelumnya. Jadi hasil sebelumnya harus disimpan. Anda dapat menggunakan ivy-resumedari ivyatau helm-resumedarihelm

  • Ketika Anda menyimpan hasil grepped sebelumnya, konteks hasil sebelumnya juga harus disimpan. Misalnya, dalam kode Solusi 2. default-directoryadalah konteks. Lihat https://github.com/abo-abo/swiper/issues/591 untuk rincian lebih lanjut.

  • Ekspresi reguler yang diperluas harus digunakan karena lebih sederhana dan sudah digunakan oleh counsel-git-grepdan the_silver_searcher / ag.

chen bin
sumber
4

Saya ingin menambahkan solusi lain ke solusi @chen bin yang juga menggunakan counsel(tetapi Anda tidak perlu memiliki proyek yang dikelola gituntuk ini).

Anda perlu menginstal ag (pencari perak) dan counselmemberikan perintah counsel-agyang mencari direktori saat ini untuk string yang dimasukkan.

Jika Anda ingin mencari di dalam suatu proyek (bukan hanya direktori kerja saat ini), tambahkan ini ke .emacs: -

  (defun rag/counsel-ag-project-at-point ()
    "use counsel ag to search for the word at point in the project"
    (interactive)
    (counsel-ag (thing-at-point 'symbol) (projectile-project-root)))

Fungsi ini akan digunakan aguntuk mencari direktori root proyek secara rekursif.

Sunting : fungsi di atas default ke simbol pada titik untuk pencarian. Jika Anda tidak menyukai perilaku itu, ganti (thing-at-point 'symbol)dengan nil. Dan jika Anda ingin hasil pencarian di buffer pers itu sendiriC-c C-o

Sunting2 : jika Anda sudah ripgrepmenginstal, Anda dapat mengganti counsel-agdengan counsel-rgdi fungsi di atas dan itu akan sama saja :)

Chakravarthy Raghunandan
sumber
Apakah projectile-project-rootbisa digunakan untuk menemukan root proyek Rails?
Boris Stitnicky
Ya, selama itu adalah proyek proyektil yang valid, itu akan berhasil. Ada beberapa paket yang disebut proyektil-rel saya pikir.
Chakravarthy Raghunandan
Mengapa Anda memberi nama fungsinya rag/...?
Boris Stitnicky
Ini adalah gaya umum yang sering digunakan orang ketika menulis fungsi mereka sendiri (Anda dapat melihatnya sedang dilakukan oleh banyak orang lain jika Anda menelusuri konfigurasi emacs mereka). Semua fungsi yang saya definisikan dimulai dengan rag/awalan dan itu membuatnya mudah ditemukan di bawah M-x:)
Chakravarthy Raghunandan
Ic, itu namamu :-)
Boris Stitnicky
2

Berikut adalah contoh fungsi grep kustom yang secara rekursif mengambil konten file (menggunakan regexp untuk istilah pencarian) dalam direktori dan subdirektori, dan menampilkan hasilnya dengan garis konteks sebelum dan sesudah. Built-in compile.eldan grep.elperpustakaan menyediakan beberapa metode untuk melompat ke lokasi di file yang berisi hasil pencarian. Tidak perlu menginstal apa pun agar contoh ini berfungsi - yang diperlukan hanyalah versi lengkap Emacs dan komputer yang memiliki greputilitas yang diinstal. Variabel grep-programdapat disesuaikan untuk menunjuk ke lokasi tertentu dari greputilitas jika standarnya tidak memadai.

(defun recursive-grep ()
"Recursively grep file contents.  `i` case insensitive; `n` print line number;
`I` ignore binary files; `E` extended regular expressions; `r` recursive"
(interactive)
  (let* ((search-term (read-string "regex:  "))
         (search-path
           (directory-file-name (expand-file-name (read-directory-name "directory:  "))))
         (default-directory (file-name-as-directory search-path))
         (grep-command
           (concat
             grep-program
             " "
             "-inIEr --color=always -C2"
             " "
             search-term
             " "
             search-path)))
    (compilation-start grep-command 'grep-mode (lambda (mode) "*grep*") nil)))
daftar hukum
sumber
Meskipun pertanyaannya tidak mencari cara memodifikasi secara bersamaan beberapa file yang dikembalikan sebagai hasil oleh fungsi grep di atas, saya merasa sangat membantu untuk menggunakan multiple-cursorsdan wgrep. Itu membuat memodifikasi beberapa file dalam satu gerakan mudah. Namun, pustaka yang disebutkan harus diinstal jika fitur-fitur itu diinginkan.
hukum
Apa keuntungan menggunakan ini dari builtin rgrep?
npostavs
1
@npostavs - Saya menemukan built-in rgrepmemakan waktu untuk menyesuaikan dengan kriteria pencarian regex pilihan saya, dan saya memilih untuk melakukan hard-code segala sesuatu dalam fungsi kustom (yang saya gunakan beberapa kali setiap hari). Jika Anda ingin menulis jawaban alternatif yang menjelaskan cara menyesuaikan rgrep, itu akan membantu pembaca forum lain di masa mendatang.
hukum