Использование Emacs для рекурсивного поиска и замены в текстовых файлах, которые еще не открыты

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

В Ultraedit я бы сделал Alt + s, затем p, чтобы отобразить диалоговое окно с параметрами: Find (включает в себя использование регулярных выражений в нескольких строках), Replace with, In Files/Types, Directory, Match Case, Match Whole Word Только, список измененных файлов и подкаталогов поиска. Обычно я сначала использую мышь для перетаскивания мышью, чтобы выбрать текст, который я хочу заменить.

Используя только сам Emacs (в Windows XP), не вызывая никакой внешней утилиты, как заменить все foo\nbar на bar\nbaz в файлах *.c и *.h в некоторой папке и всех папках под ней. Возможно, Emacs не лучший инструмент для этого, но как это можно сделать с минимальной командой?

Ответ 1

  • M-x find-name-dired: вам будет предложено создать корневой каталог и шаблон имени файла.
  • Нажмите t, чтобы "переключить отметку" для всех найденных файлов.
  • Нажмите Q для "Query-Replace in Files...": вам будут предложены регулярные выражения запроса/подстановки.
  • Перейдите к query-replace-regexp: SPACE, чтобы заменить и перейти к следующему совпадению, n, чтобы пропустить совпадение и т.д.
  • Нажмите C-x s, чтобы сохранить буферы. (Затем вы можете нажать y, n или !, чтобы сохранить все сразу)

Ответ 2

  • M-x find-name-dired RET
    • может потребоваться некоторое время для отображения всех файлов в списке, прокрутите вниз (M->), пока не появится "find finished", чтобы убедиться, что все они загрузили
  • Нажмите t, чтобы "переключить отметку" для всех найденных файлов.
  • Нажмите Q для "Query-Replace in Files...": вам будут предложены регулярные выражения запроса/подстановки.
  • Действуйте так же, как с помощью query-replace-regexp: SPACE или y для замены и перехода к следующему совпадению, n для пропуска соответствия и т.д.
    • Тип! для замены всех вхождений в текущем файле без запроса, N пропустить всю возможную замену для остальной части текущего файла. (N только emacs 23+)
    • Чтобы выполнить замену на все файлы без дальнейшего запроса, введите y.
  • Вызов "ibuffer" (C-x C-b, если привязан к ibuffer или M-x ibuffer RET), чтобы отобразить все открытые файлы.
  • Введите * u, чтобы отметить все несохраненные файлы, введите S, чтобы сохранить все отмеченные файлы.
  • * * RET, чтобы удалить все отметки или введите D, чтобы закрыть все отмеченные файлы.

Этот ответ объединен из этого ответа, от этого сайта, и из моих собственных заметок. Использование Emacs 23 +.

Ответ 3

Обычно я использую другие инструменты для выполнения этой задачи, и, похоже, многие из подходов, упомянутых в EmacsWiki Find and Replace Across Files entry shell но Findr Package выглядит очень многообещающе.

Кража части исходного файла:

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.

Ответ 4

Полученные ответы являются замечательными, однако я подумал, что добавлю несколько иной подход.

Это более интерактивный метод и требует wgrep, rgrep и iedit. Оба iedit и wgrep должны быть установлены через MELPA или Marmalade (используя M-x package-list-packages)

Сначала запустите M-x rgrep, чтобы найти нужную строку.

Вы сможете указать типы файлов/шаблон и папку для рекурсии.

Затем вам нужно запустить wgrep запустите его с помощью C-s C-p.

Wgrep позволит вам отредактировать результаты rgrep, поэтому установите область в строке для соответствия и запустите iedit-mode с помощью C-; (в зависимости от вашего терминала вам может потребоваться повторная привязка)

Все вхождения будут редактироваться сразу. C-x C-s совершить wgrep. Затем C-x s ! сохранить измененные файлы.

Основное преимущество этого метода заключается в том, что вы можете использовать iedit-mode для переключения определенных совпадений M-;. Вы также можете использовать результаты в rgrep для перехода в файлы, например, если у вас есть неожиданное совпадение.

Я нахожу это очень полезным для редактирования исходных текстов и переименования символов (переменных, имен функций и т.д.) в проекте.

Если вы еще не знаете/используете iedit mode, это очень удобный инструмент, я настоятельно рекомендую вам взглянуть на него.

Ответ 5

Использование устаревших для рекурсии глубокого дерева каталогов будет немного медленным для этой задачи. Вы можете использовать tags-query-replace. Это означает, что вы должны создать таблицу тегов, но это часто полезно в любом случае, и это быстро.

Ответ 6

Снаряд действительно хорош: C-c p r запускает команду projectile-replace

Ответ 7

M-X Dired и t, чтобы отметить все файлы, и Q для запроса заменить текст во всех них. Вы можете развернуть подкаталог с помощью команды i перед заменой запроса. Их ключевая информация, которую я добавляю, заключается в том, что если вы дадите префикс (control-u) команде i, он предложит вам аргумент arg, а аргумент -R будет рекурсивно расширять все subdirs в буфер dired. Итак, теперь вы можете запросить поиск каждого файла в целом каталоге.

Ответ 8

Для открытых буферов это то, что я делаю:

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

Ответ 9

Источник информации: 1

Для пользователей emacs pro:

  • Вызвать, чтобы отобразить файлы в каталоге или вызвать find-dired, если вам нужны все подкаталоги.
  • Отметьте нужные файлы. Вы можете пометить по регулярному выражению, набрав 【% m】.
  • Введите Q для вызова dired-do-query-replace-regexp.
  • Введите ваше выражение regex и замените строку. 〔☛ общий шаблон регулярного выражения elisp〕
  • В каждом случае введите y, чтобы заменить n, чтобы пропустить. Введите 【Ctrl + g】, чтобы прервать всю операцию.
  • Тип! заменить все вхождения в текущем файле без запроса, N пропустить все возможные замены для остальной части текущего файла. (N только emacs 23)
  • Чтобы выполнить замену на все файлы без дальнейшего запроса, введите Y. (только Emacs 23)
  • Вызовите ibuffer, чтобы перечислить все открытые файлы. Введите 【* u】, чтобы отметить все несохраненные файлы, введите S для сохранения всех отмеченных файлов, введите D, чтобы закрыть их все.

Пошаговое руководство для начинающих Emacs

Выберите целевые файлы

Запустите emacs, введя "emacs" в командной строке командной строки. (Или дважды щелкните значок Emacs, если вы находитесь в среде графического интерфейса пользователя)

Выбор файлов в каталоге

Сначала вам нужно выбрать файлы, которые вы хотите заменить. Используйте графическое меню 〖 "Файл" ▸ "Открыть каталог" 〗. Emacs запросит у вас путь к каталогу. Введите путь к каталогу, затем нажмите Enter.

Теперь вам будет показан список файлов, и теперь вам нужно будет пометить файлы, которые вы хотите, чтобы найти/заменить регулярное выражение. Вы помечаете файл, перемещая курсор в нужный файл, затем нажмите m. Снять отметку, нажав u. (Чтобы перечислить подкаталоги, переместите курсор в каталог и нажмите i. Содержимое подкаталога будет указано внизу.) Чтобы пометить все файлы регулярным выражением, введите 【% m】, затем введите шаблон регулярного выражения. Например, если вы хотите пометить все файлы HTML, введите 【% m】, затем .html $. (Вы можете найти список команд меток в графическом меню "Марк" (это меню появляется, когда вы находитесь в режиме ожидания).

Выбор файлов в каталоге и всех его подкаталогах

Если вы хотите найти/заменить на файлы внутри каталога, включая сотни подкаталогов, здесь вы можете выбрать все эти файлы.

Вызов поиска. (вы вызываете команду, нажимая 【Alt + x】) Затем введите имя каталога, ⁖/Users/mary/myfiles

Примечание. Если вы используете emacs в текстовом терминале unix unix, и если 【Alt + x】 не работает, то эквивалентный ход клавиши - Esc Esc.】.

Emacs спросит вас с приглашением "Run find (with args):". Если вам нужно сделать замену на всех HTML файлах, тогда введите -name "* html". Если вам неважно, какой файл, но просто все файлы под этим каталогом, тогда дайте "-type f".

Теперь отметьте файлы, как описано выше.

Интерактивный поиск/замена

Теперь вы готовы выполнить замену интерактивного поиска. Для простоты позвольте сказать, что вы просто хотите заменить слово "быстрый" на "супер". Теперь вызовите dired-do-query-replace-regexp. Он предложит вам строку регулярных выражений и строку замены. Введите "быстрый", введите, затем "супер".

Теперь emacs будет использовать ваш шаблон и проверять файлы, а также останавливаться и показывать вам всякий раз, когда происходит совпадение. Когда это произойдет, emacs предложит вам, и у вас есть выбор внести изменения или пропустить изменение. Чтобы внести изменения, введите y. Чтобы пропустить, введите n. Если вы просто хотите emacs идти вперед и вносить все такие изменения в текущий файл, введите!.

Если вы хотите отменить всю операцию без сохранения внесенных изменений, введите 【Ctrl + g】, затем выйдите из emacs, используя меню 〖Файл ▸ Выход из Emacs〗.

Сохранение измененных файлов

Теперь, после того, как вы прошли вышеупомянутое испытание, вам нужно сделать еще один шаг, и это сохранение измененных файлов.

Если вы используете emacs версии 22 или новее, тогда позвоните ibuffer, чтобы перейти в режим перечисления буфера, затем введите 【* u】, чтобы пометить все несохраненные файлы, затем введите S, чтобы сохранить их все. (этот сдвиг-с)

Если вы используете версию emacs 21, вы можете сделать это: вызвать список-буферы, затем переместить курсор в файл, который вы хотите сохранить, и ввести s. Он помечает файл для последующего сохранения. Введите u, чтобы снять отметку. После того, как вы закончите, введите x, чтобы выполнить сохранение всех файлов, отмеченных для сохранения. (в emacs открытый файл называется "buffer". Игнорирование других вещей там.)

Альтернативу вышеуказанным параметрам вы также можете вызвать save-some-buffers 【Ctrl + x s】. Затем emacs отобразит каждый несохраненный файл и спросит, хотите ли вы его сохранить.

Примечание: emacs regex - это не то же самое, что Perl или Python, но похоже. Краткие и общие шаблоны см. В Emacs Regex.

Ответ 10

Другим вариантом является использование Поиск сосульков. Это разный вид инкрементного поиска, который использует завершение ввода вашего минибуфера против поисковых запросов. Когда вы изменяете свой текущий вход, набор совпадающих удалений обновляется в буфере *Completions*.

Вы можете искать любое количество файлов, буферов или закладки, которые вы можете выбрать с помощью соответствия шаблону минибуфера (например, regexp).

Когда вы посещаете хит поиска, вы можете replace по запросу либо весь хит, либо только его часть, которая соответствует вашему текущему минибуферу вход. Замена по требованию означает, что вы не будете спрашивать о каждом ударе поиска по очереди; вы получаете доступ к хитам, которые вы хотите напрямую, в любом порядке. Этот подход может быть более эффективным, чем замена запроса, если у вас есть ограниченное количество замен: вы пропустите исчерпывающую подсказку y/n.

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

Вы также можете сортировать поисковые запросы в разных заказах сортировки для облегчения доступа/навигации.

Ответ 11

Я хотел бы предложить еще один отличный инструмент, который еще не упоминался, а именно Helm.

Это отличная замена для многих стандартных операций Emacs, включая завершение, поиск и т.д. В частности, helm-find-files позволяет выполнять замену запроса (в том числе regexp) в нескольких выбранных файлах.

Просто откройте helm-find-files, пометьте соответствующие файлы M-SPC, а затем используйте F6 или F7, чтобы выполнить замену запроса или запрос заменить regexp в выбранных файлах.

Ответ 12

find-name-dired в порядке, но:

  • Все файлы, которые вы получаете, совпадают, одно регулярное выражение.
  • find-dired является более гибким в этом отношении, но он также предназначен для использования общих правил (даже если они могут быть произвольно сложными). И, конечно, find имеет свой собственный сложный язык.
  • если вы хотите действовать только на некоторые из файлов, чьи имена были собраны в буфере find(-name)-dired, вам нужно либо пометить их, либо удалить/опустить строки тех, на которых вы не хотите действовать.

Альтернативой является использование Dired + команд, которые действуют на (a) отмеченные файлы и (b) все помеченные файлы (или все файлы, если они не отмечены) в отмеченных подкаталогах... найдены рекурсивно. Это дает вам как общность, так и простое управление выбором файлов. Эти команды "здесь и ниже" находятся на префиксном ключе M-+ в режиме Dired.

Например, M-+ Q совпадает с Q --- query-replace, но целевые файлы - все те, которые помечены в текущем каталоге и в любых отмеченных поддиректорах, вниз, вниз, вниз...

Да, альтернатива использованию таких команд здесь и ниже заключается в том, чтобы вставить все поддиры и их поддиры, рекурсивно, а затем использовать команду верхнего уровня, например Q. Но часто бывает удобно не беспокоиться о вставленных subdirs.

И для этого вам все равно нужен быстрый способ вставить все такие поддиры рекурсивно. Здесь также может помочь Dired +. M-+ M-i вставляет все отмеченные поддиры и их собственные помеченные поддиры, рекурсивно. То есть, он похож на M-i (который вставляет отмеченные поддиры в Dired +), но он действует рекурсивно на поддиры.

(Все такие команды "здесь и ниже" Dired + находятся в меню Несколько > Отмечено здесь и ниже.)

Вы также можете выполнять Dired-операции на наборе файлов Emacs, который представляет собой сохраненный набор имен файлов, расположенных в любом месте. И если вы используете Icicles, тогда вы можете открыть Dired buffer только для файлов в наборе файлов или других типах сохраненных списки файлов.

Вы также можете закладить любой Dired buffer, в том числе тот, который вы создаете с помощью find(-name)-dired. Это дает вам быстрый способ вернуться к такому набору (например, к набору проектов) позже. И если вы используете Закладка +, то закладок записей Dired buffer записываются (a) его переключатели ls, (b), которые файлы отмечены, (c) какие подкаталоги вставлены и (d) какие (под) каталоги скрыты. Все это восстанавливается, когда вы "прыгаете" в закладку. Закладка + также позволяет вам закладок всего дерева буферов Dired - переход на закладку восстанавливает все буферы в дереве.

Ответ 13

Это не Emacs, но xxdiff поставляется с инструментом, называемым xx-rename, который будет делать это для нескольких строк за раз (например, From To from to FROM TO TO), с интерактивным приглашением, сохранять резервные копии всех измененных файлов и создать короткий журнал изменений, внесенных в контекст. Это то, что я обычно использую, когда делаю большие/глобальные переименования.