จะค้นหาเนื้อหาไฟล์ / grep ซ้ำในไดเรกทอรี / ไดเรกทอรีย่อยใน Emacs ได้อย่างไร?


14

ฉันใช้ Emacs อยู่พักหนึ่งและฉันก็ยังไม่เข้าใจว่าอะไรคือวิธีที่ดีที่สุดในการ grep (วนซ้ำ) สำหรับสตริงภายในไดเรกทอรี (โครงการ) และได้รับผลลัพธ์ที่แสดงเป็นบัฟเฟอร์ที่ต้องการหรือมากกว่านั้น วิธีที่มีประโยชน์ โปรดให้ความกระจ่างแก่ฉัน


คุณเคยลองใช้helm-projectile-grepคำสั่ง (ถ้าคุณติดตั้ง projectile helm) หรือprojectile-grepไม่?
Chakravarthy Raghunandan

ผมไม่ทราบว่าสิ่งที่helmหรือprojectileเป็น แต่ถ้ามันเป็นเครื่องมือที่แนะนำผมจะติดตั้งและเรียนรู้มัน
Boris Stitnicky

projectile-grepใช่กระสุนปืนเป็นแพคเกจที่มีวิธีการง่ายที่จะทำสิ่งที่คุณกล่าวถึงในคำถามที่มีคำสั่ง นอกจากนี้ฉันขอแนะนำให้คุณติดตั้งhelmแพ็คเกจ ฉันนึกภาพไม่ออกว่าวิ่ง emacs ไปhelmและhelm-projectileไม่ คุณอาจสนใจhelm-agแพ็คเกจ (ซึ่งมีอินเตอร์เฟส helm สำหรับ silver searcher ใน emacs ซึ่งควรจะเร็วกว่า grep หรือ ack)
Chakravarthy Raghunandan

2
เพื่อให้คำถามมีประโยชน์มากขึ้นและง่ายต่อการค้นหาสำหรับ Google และผู้ค้นหาฟอรัมในอนาคตอาจพิจารณาแก้ไขชื่อของคำถามเพื่อรวมคำซ้ำและพิจารณา จำกัด ขอบเขต - เช่นวิธีการ grep เนื้อหาไฟล์ซ้ำในไดเรกทอรี / ไดเรกทอรีย่อย นั่นเป็นเพียงตัวอย่าง - มันอาจเป็นอย่างอื่นแทน
ฏหมาย

1
ในขณะที่ความคิดเห็นและคำตอบจนถึงขณะนี้ได้เสนอแพคเกจจำนวนมากให้ทำในรูปแบบที่หลากหลาย แต่ฉันไม่เห็นจากคำถามของคุณว่าทำไมเรื่องง่าย ๆM-x grepไม่ได้ทำในสิ่งที่คุณต้องการ มันช่วยให้คุณให้บรรทัดคำสั่งโดยพลการบางครั้งฉันใช้ที่findนั่นเมื่อฉันต้องการการสอบถามซ้ำ
แผนที่

คำตอบ:


15

เนื่องจากคำถามเดิมไม่ได้กล่าวถึงrgrepฉันจะดำเนินการต่อและชี้ไปที่นี่ นี่เป็นตัวเลือกที่ง่ายที่สุดที่ติดตั้งไว้แล้วใน Emacs และฉันพบว่ามันเพียงพอสำหรับทุกสิ่ง

จากเอกสาร

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

การค้นหาถูก จำกัด เฉพาะชื่อไฟล์ที่ตรงกับรูปแบบเปลือกไฟล์

ดังนั้นสำหรับตัวอย่างเช่นในการค้นหาไฟล์หลามทั้งหมดภายใต้ไดเรกทอรีบ้านของฉันสำหรับการใช้งานของsome_functionผมจะเรียกมันว่าด้วยsome_function, *.py, ~/เป็นข้อโต้แย้ง ผลลัพธ์จะแสดงในบัฟเฟอร์ใหม่


1
อาจต้องการพูดถึงfind-grep-diredเช่นกันเนื่องจาก OP กล่าวถึง "ผลลัพธ์ที่แสดงเป็นบัฟเฟอร์แบบ dired หรือ ... "
npostavs

@npostavs ฉันไม่ได้ใช้find-grep-diredตัวเองอย่าลังเลที่จะเพิ่มลงในคำตอบ
2699

ฉันเคยทดลองกับสิ่งที่ชอบของแอ๊กและเอจีบนสมมติฐานที่ว่าฉันควรจะเปลี่ยนจาก rgrep เป็นสิ่งที่ใช้พวกนั้นเพราะพวกเขาควรจะดีกว่า ในที่สุดฉันก็อยู่กับ rgrep เพราะฉันไม่พบประโยชน์ในการเปลี่ยนแปลง! ในบรรทัดคำสั่งมีกรณีที่ต้องทำอย่างแน่นอน แต่ใน Emacs rgrepทำงานได้ดีมากในการกำหนดเป้าหมายการค้นหาว่าไม่มีความแตกต่างด้านความเร็วที่สำคัญระหว่างพวกเขา (ผมอยากจะบอกว่าเป็น rgrep fractionally ได้เร็วขึ้นในบางกรณี แต่ผมจำไม่ได้แน่นอน.)
Phils

1
โปรดทราบว่าnext-errorและprevious-errorสามารถใช้เพื่อนำทางผ่านชุดผลลัพธ์ ฉันค้นหาM-g M-nและM-g M-pสะดวกที่สุดของการผูกแบบมาตรฐาน
phils

find-grep-diredไม่ให้บัฟเฟอร์กับลิงก์อ้างอิงข้าม rgrepทำเพื่อฉันและคำแนะนำสุดท้ายเกี่ยวกับnext-error previous-errorมันวิเศษมาก! การเล่นโวหารเดียวของฉันคือการส่งออกคำสั่งยุ่งทั้งหมดที่จุดเริ่มต้นของบัฟเฟอร์
เบิร์ต

11

ฉันใช้อย่างมีความสุขM-x find-grep-diredมานานหลายปี มันส่งคืนผลลัพธ์เป็นบัฟเฟอร์ dired ซึ่งคุณสามารถใช้


ฉันมีปัญหาในการเชื่อมโยงการอ้างอิงโยงเพื่อทำงานกับสิ่งนี้ การกำหนดค่าของมันค่อนข้างน่ารังเกียจ ( find-ls-option) และหลังจากนั้นคุณก็จบลงด้วยบางสิ่งที่ค่อนข้างละเอียดเพราะมันใช้งานไม่ได้หากไม่มีวันที่ก่อนชื่อไฟล์!
เบิร์ต

5

โซลูชันที่ 1 (ทางออกที่ดีที่สุด):

ติดตั้งที่ปรึกษา ( https://github.com/abo-abo/swiper/blob/master/c Counsel.el )

M-x counsel-git-grepแล้วก็

ไม่จำเป็นต้องตั้งค่า (git รู้จักรูทโปรเจ็กต์และไฟล์ที่จะไม่รวม) ทั้งgit grepและcounselมีประสิทธิภาพ

จำเป็นต้องมีการจัดการโครงการโดย git

ที่ปรึกษาต้องใช้โหมด ivy

โซลูชันที่ 2:

โซลูชันนี้ใช้ grep และทำงานกับโครงการใด ๆ มันด้อยกว่าโซลูชัน 1 เนื่องจากช้ากว่าและต้องตั้งค่าด้วยตนเอง มันขึ้นอยู่กับโหมด ivy

(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))))))))

คุณต้องสร้าง. dir-locals.el เพื่อตั้งค่าsimple-project-rootดูhttps://www.gnu.org/software/emacs/manual/html_node/emacs/Directory-Variables.htmlเพื่อดูรายละเอียดทางเทคนิค

โค้ดในโซลูชัน 2 เป็นเพียงต้นแบบ การใช้งานจริงของฉันมีความซับซ้อนมากขึ้น ดูcounsel-etags-grepในhttps://github.com/redguardtoo/c Counsel-etags/blob/master/c Counsel-etags.el

สรุป:

นี่เป็นทางออกที่ดีที่สุดสองข้อที่ฉันรู้

หากมีวิธีแก้ปัญหาอื่นที่ดีกว่าพวกเขาต้องการอย่างน้อยแก้ปัญหาด้านล่างให้พร้อมผลิต

  • วิธีรับคีย์เวิร์ดเพื่อ grep (เช่นรับคีย์เวิร์ดจากภูมิภาคที่เลือก)

  • หลบหนีคำหลัก

  • หากมีโปรแกรม grep ที่มีประสิทธิภาพมากขึ้นเราควรใช้มัน (ripgrep, the_silver_searcher / ag, ... ฯลฯ ) หรือมิฉะนั้นให้เลือก grep เริ่มต้น

  • หน้าต่างตัวเลือกควรใช้เต็มความกว้างของหน้าจอและผู้ใช้สามารถกรองตัวเลือกแบบโต้ตอบ

  • เราควรแสดงเส้นทางสัมพัทธ์ในหน้าต่างตัวเลือก

  • สามารถใช้ผลลัพธ์ grepped ก่อนหน้านี้ได้อีกครั้ง ดังนั้นควรบันทึกผลลัพธ์ก่อนหน้า คุณสามารถใช้ivy-resumeจากivyหรือhelm-resumeจากhelm

  • เมื่อคุณบันทึกผลลัพธ์ grepped ก่อนหน้าบริบทของผลลัพธ์ก่อนหน้าควรถูกบันทึกด้วย ตัวอย่างเช่นในรหัสของโซลูชัน 2 default-directoryคือบริบท ดูhttps://github.com/abo-abo/swiper/issues/591สำหรับรายละเอียดเพิ่มเติม

  • ควรใช้นิพจน์ทั่วไปเพิ่มเติมเนื่องจากง่ายกว่าและถูกใช้แล้วโดยcounsel-git-grepthe_silver_searcher / ag


4

ฉันต้องการเพิ่มโซลูชันอื่นให้กับโซลูชันของ @chen bin ซึ่งใช้เช่นกันcounsel(แต่คุณไม่จำเป็นต้องดูแลรักษาโครงการgitนี้ด้วย)

คุณต้องติดตั้งag (ตัวค้นหาซิลเวอร์) และcounselให้คำสั่งcounsel-agที่ค้นหาไดเรกทอรีปัจจุบันสำหรับสตริงที่ป้อน

หากคุณต้องการค้นหาภายในโครงการ (ไม่ใช่เพียงไดเรกทอรีการทำงานปัจจุบัน) เพิ่มสิ่งนี้ใน.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)))

ฟังก์ชันนี้จะใช้agเพื่อค้นหาไดเรกทอรีรากของโปรเจ็กต์ซ้ำ

แก้ไข : ฟังก์ชั่นด้านบนเริ่มต้นที่สัญลักษณ์ที่จุดสำหรับการค้นหา ถ้าคุณไม่ชอบพฤติกรรมที่เปลี่ยนด้วย(thing-at-point 'symbol) nilและถ้าคุณต้องการผลการค้นหามันเป็นบัฟเฟอร์กดเองC-c C-o

แก้ไข 2 : ถ้าคุณripgrepติดตั้งแล้วคุณสามารถแทนที่counsel-agด้วยcounsel-rgในฟังก์ชั่นด้านบนและมันก็จะเป็นเรื่องดี :)


สามารถprojectile-project-rootใช้ในการค้นหารูทโปรเจ็กต์ของ Rails ได้หรือไม่
Boris Stitnicky

ใช่ตราบใดที่มันเป็นโครงการกระสุนที่ถูกต้องมันจะทำงานได้ มีบางแพ็คเกจที่เรียกว่า projectile-rails ที่ฉันคิดว่า
Chakravarthy Raghunandan

คุณไม่ตั้งชื่อฟังก์ชั่นที่ว่าทำไมrag/...?
Boris Stitnicky

เป็นสไตล์ทั่วไปที่ผู้คนมักใช้เมื่อเขียนฟังก์ชั่นของตัวเอง (คุณสามารถเห็นได้ว่ามันถูกทำโดยคนอื่น ๆ มากมายถ้าคุณเรียกดูการกำหนดค่า emacs) ฟังก์ชั่นทั้งหมดที่ฉันกำหนดไว้เริ่มต้นด้วยrag/คำนำหน้าและทำให้ง่ายต่อการค้นหาภายใต้M-x:)
Chakravarthy Raghunandan

ใช่, นั่นคือชื่อของคุณ :-)
Boris Stitnicky

2

นี่คือตัวอย่างของฟังก์ชัน grep ที่กำหนดเองที่ greps เนื้อหาไฟล์ซ้ำ (ใช้ regexp สำหรับคำค้นหา) ในไดเรกทอรีและไดเรกทอรีย่อยและแสดงผลลัพธ์ที่มีบรรทัดบริบทก่อนและหลัง บิวด์อินcompile.elและgrep.elไลบรารีจัดเตรียมสำหรับหลายวิธีเพื่อข้ามไปยังตำแหน่งในไฟล์ที่มีผลลัพธ์การค้นหา ไม่จำเป็นต้องติดตั้งอะไรอีกเพื่อให้ตัวอย่างนี้ใช้งานได้ - สิ่งที่จำเป็นทั้งหมดคือ Emacs เวอร์ชันเต็มและคอมพิวเตอร์ที่grepติดตั้งยูทิลิตี้ ตัวแปรgrep-programสามารถปรับให้ชี้ไปที่ตำแหน่งเฉพาะของgrepยูทิลิตีหากค่าเริ่มต้นไม่เพียงพอ

(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)))

ขณะที่คำถามที่ไม่แสวงหาวิธีการที่จะปรับเปลี่ยนไปพร้อม ๆ กันหลายไฟล์ที่จะกลับมาเป็นผลโดยฟังก์ชัน grep ข้างต้นผมพบว่ามันเป็นประโยชน์มากที่จะใช้และmultiple-cursors wgrepมันทำให้การปรับเปลี่ยนไฟล์หลายไฟล์ในครั้งเดียวก้มง่าย อย่างไรก็ตามไลบรารีดังกล่าวจะต้องได้รับการติดตั้งหากต้องการคุณลักษณะเหล่านั้น
ฏหมาย

อะไรคือข้อดีของการใช้สิ่งนี้ในตัวrgrep?
npostavs

1
@npostavs - ฉันพบว่าrgrepต้องใช้เวลานานในการปรับแต่งตามเกณฑ์การค้นหา regex ที่ฉันต้องการและฉันเลือกที่จะเขียนโค้ดทุกอย่างในฟังก์ชันที่กำหนดเอง (ซึ่งฉันใช้หลายครั้งในแต่ละวัน) หากคุณต้องการที่จะเขียนคำตอบทางเลือกที่อธิบายถึงวิธีการปรับแต่งrgrepนั้นจะเป็นประโยชน์กับผู้อ่านฟอรั่มอื่น ๆ ในอนาคต
กฎหมาย
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.