Как рекурсивно искать / grep содержимое файла в каталоге / подкаталогах в Emacs?

14

Я использую Emacs в течение достаточно долгого времени, и я до сих пор почему-то не могу понять, каков наилучший (рекурсивно) grep для строки в каталоге (проекта) и получения результатов, представленных в виде буфера с задержкой или в некоторых других полезный способ. Пожалуйста, просветите меня.

Борис Стиницкий
источник
Вы пробовали использовать helm-projectile-grepкоманду (если у вас установлен снаряд шлем) или projectile-grep?
Чакраварти Рагхунандан
Я не знаю, что helmили projectileесть, но если это рекомендуемый инструмент, я бы установил и изучил его.
Борис Стиницкий,
Да, projectile - это пакет, который предоставляет простой способ сделать то, что вы упомянули в вопросе, с помощью команды projectile-grep. Также я НАСТОЯТЕЛЬНО рекомендую вам установить helmпакет. Я не могу представить себе запуск Emacs без helmи helm-projectile. Вы также можете быть заинтересованы в helm-agпакете (который предоставляет интерфейс helm для поисковика серебра в emacs, который предположительно быстрее, чем grep или ack).
Чакраварти Рагхунандан
2
Чтобы вопрос был более полезным и его было легче найти для будущих пользователей Google и форумчан, возможно, стоит подумать об изменении названия вопроса, чтобы включить слово рекурсивно , и рассмотреть вопрос об ограничении области видимости, например, Как рекурсивно grep содержимое файла в каталоге. / подкаталоги . Это просто пример - это может быть что-то другое.
юрист
1
Хотя в комментариях и ответах до сих пор предлагалось несколько пакетов, чтобы сделать это различными причудливыми способами, из вашего вопроса я не вижу, почему simple M-x grepне делает то, что вам нужно. Это даст вам произвольную командную строку, которую я иногда использую findтам, когда мне нужна рекурсия.
КАРТА

Ответы:

15

Ну, поскольку оригинальный вопрос не упоминается rgrep, я пойду дальше и укажу здесь. Это самый простой вариант, уже встроенный в Emacs, и я обнаружил, что его более чем достаточно для большинства вещей.

Из документации,

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

Поиск ограничен именами файлов, соответствующими шаблону оболочки FILES.

Так, например, чтобы найти все файлы питона под моей домашней директории для использования some_function, я бы назвал его some_function, *.py, в ~/качестве аргументов. Результаты отображаются в новом буфере.

user2699
источник
1
find-grep-dired
Возможно, также стоит
@npostavs Я не использовал find-grep-diredсебя, не стесняйтесь добавлять его в ответ.
user2699
Однажды я экспериментировал с подобными ack и ag, предполагая, что я должен перейти с rgrep на что-то, использующее их, потому что они должны были быть лучше. В конце концов я остался с rgrep, потому что не нашел никакой пользы от изменений! В командной строке, безусловно rgrep, есть смысл , но в Emacs такая хорошая работа направлена ​​на поиск, что между ними не было значительных различий в скорости. (Я хочу сказать, что в некоторых случаях rgrep был немного быстрее , но точно не помню.)
phils
1
Обратите внимание, что next-errorи previous-errorможет использоваться для навигации по набору результатов. Я нахожу M-g M-nи M-g M-pсамые удобные из стандартных привязок.
Филс
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/counsel.el )

Потом M-x counsel-git-grep.

Никаких настроек не требуется (git знает корень проекта и файлы для исключения). И то, git grepи другое counselэффективно.

Проект должен управляться git.

адвокат требует режима плюща.

Решение 2:

Это решение использует grep и работает на любом проекте. Он уступает Solution 1, потому что он медленнее и требует ручной настройки. Он также основан на режиме плюща.

(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/counsel-etags/blob/master/counsel-etags.el

Резюме:

Это два лучших решения, которые я знаю.

Если существуют какие-либо другие лучшие решения, они должны по крайней мере решить следующие проблемы, чтобы быть готовыми к производству,

  • как получить ключевое слово для grep (например, получить ключевое слово из выбранного региона)

  • избежать ключевого слова

  • если существует более эффективная программа grep, мы должны использовать ее (ripgrep, the_silver_searcher / ag, ... и т. д.), либо использовать альтернативный вариант grep по умолчанию

  • Окно кандидата должно использовать всю ширину экрана, и пользователи могут фильтровать кандидатов в интерактивном режиме (именно поэтому люди используют плющ или шлем)

  • мы должны показать относительный путь в окне кандидата

  • возможность повторно использовать предыдущий результат. Таким образом, предыдущий результат должен быть сохранен. Вы можете использовать ivy-resumeот ivyили helm-resumeотhelm

  • Когда вы сохраняете предыдущий полученный результат, контекст предыдущего результата также должен быть сохранен. Например, в коде решения 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

Edit2 : если вы ripgrepустановили, вы можете заменить counsel-agс counsel-rgв приведенной выше функции , и это будет так же , как нормально :)

Чакраварти Рагхунандан
источник
Можно ли projectile-project-rootнайти корневой проект Rails?
Борис Ститницкий
Да, пока это действительный проект снаряда, он будет работать. Я думаю, что был какой-то пакет под названием «снарядовые рельсы».
Чакраварти Рагхунандан
Почему вы назвали функцию rag/...?
Борис Ститницкий
Это общий стиль, который люди часто используют при написании своих собственных функций (вы можете увидеть, что это делают многие другие, если вы просматриваете их конфигурацию emacs). Все функции, которые я определил, начинаются с rag/префикса, и это облегчает их поиск M-x:)
Chakravarthy Raghunandan
Ic, это твое имя :-)
Борис Stitnicky
2

Вот пример пользовательской функции grep, которая рекурсивно подбирает содержимое файла (используя регулярное выражение для поискового запроса) в каталоге и его подкаталогах и отображает результаты со строками контекста до и после. Встроенные библиотеки 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)))
lawlist
источник
Хотя вопрос не в том, чтобы одновременно модифицировать несколько файлов, которые возвращаются в результате вышеупомянутой функции grep, я считаю очень полезным использовать multiple-cursorsи wgrep. Это делает изменение нескольких файлов одним махом на одном дыхании. Указанные библиотеки, однако, должны быть установлены, если эти функции желательны.
юрист
В чем преимущество использования этого над встроенным rgrep?
npostavs
1
@npostavs - я обнаружил, что встроенная функция rgrepтребует много времени для настройки в соответствии с моими предпочтительными критериями поиска регулярных выражений, и я решил жестко закодировать все в пользовательской функции (которую я использую несколько раз каждый день). Если вы хотите написать альтернативный ответ, который описывает, как настроить rgrep, это будет полезно для других читателей форума в будущем.
юрист