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

212

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

В Ultraedit я бы сделал Alt + s, затем p, чтобы отобразить диалоговое окно с параметрами: Найти (включая использование регулярных выражений в нескольких строках), Заменить на, В файлах / типах, Каталог, Соответствие регистру, Соответствие только целому слову, Список Изменены файлы и поиск в подкаталогах. Обычно я сначала использую мышь, чтобы щелкнуть и перетащить текст, который я хочу заменить.

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

Роб Кам
источник

Ответы:

370
  1. M-x find-name-dired: вам будет предложено указать корневой каталог и шаблон имени файла.
  2. Нажмите t чтобы «пометить метку» для всех найденных файлов.
  3. Нажмите Qдля «Query-Replace in Files ...»: вам будет предложено указать регулярные выражения запроса / замены.
  4. Действуйте как query-replace-regexp: SPACEзаменить и перейти к следующему матчу, nпропустить матч и т. Д.
  5. Нажмите, C-x sчтобы сохранить буферы. (Вы можете нажать y, nили, !чтобы сохранить все сразу)
Крис Конвей
источник
10
Нет, это рекурсивно. Нерекурсивная версия была бы% m в режиме dired.
Крис Конвей
5
Возможно, потому что вы вызываете C: \ WINDOWS \ system32 \ find.exe, а не GNU find.
Джаз
6
Стин, Крис: Возможно, это не совсем то, что вы хотите, но вы можете использовать «Cx s» (save-some-buffers), он будет запрашивать вас для каждого измененного файла, поэтому вам нужно будет только нажать «y» несколько раз.
Даниэльпое
48
C-x s !сохранить все файлы одновременно.
cYrus
10
M-,продолжить поиск и заменить (например, после запроса «вернуть файл», если файл был изменен на диске).
tfischbach
37
  • 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
ibufferC-x C-bпо умолчанию не привязан
Фил
2
Ну лично я рекомендую , что люди действительно связывают C-x C-bс ibuffer, потому что это намного лучше , чем связывание по умолчанию. Просто добавьте (global-set-key (kbd "C-x C-b") 'ibuffer)в свой файл .emacs. В противном случае используйте M-x ibuffer RETприведенные выше инструкции.
Фил
хорошо. Я думаю, что у меня есть стартовый комплект emacs, поэтому мой связан
Фрэнк Хенард
1
Файл bla-bla-blaпосещается только для чтения и прекращает поиск. Как избежать / игнорировать это и продолжать замену? Спасибо.
Фелипе
3
Почему, ibufferкогда вы можете C-x s( save-some-buffers) и введите !? Я не хочу быть напыщенным, просто любопытным. PS плюс для описания !, Nи Y.
d12frosted
17

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

Это более интерактивный метод, и требует 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, это очень удобный инструмент, я настоятельно рекомендую вам взглянуть на него.

ocodo
источник
Эта техника довольно мощная, даже без использования iedit. Это превращает умение grep (легко с lgrep / rgrep) в умение массово искать-заменять!
NeilenMarais
Объединение rgrep / wgrep / macros - это круто.
ocodo
2
Связывание клавиш для начала wgrep, C-c C-pкстати.
Techniao
15

Projectile действительно хорош: Cc pr запускает команду projectile-replace

eflanigan00
источник
13

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

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

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.
Блэр Конрад
источник
Как я могу найти строку "Collectible" во всех файлах с расширением Java и HXX, используя findr.el?
swdev
@swdev: Mx findr-query-replace RET Collectible RET <замена> RET. * \. java RET <каталог для поиска в> RET
ShreevatsaR
Мне нравится этот подход (он избегает всех переключений, связанных с прямым подходом). Я использую некоторые вспомогательные функции, чтобы объединить это с метательным снарядом (проект-рут-обнаружение) и заменить вещь в точке: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ
6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Примечание: регулярное выражение в emacs не такое же, как в Perl или Python, но похожее. Сводная информация и общие шаблоны приведены в разделе: Emacs Regex.

Прашант Шарма
источник
2
Этот ответ должен сделать его более дружелюбным для новичка, чем самый популярный ответ, я надеюсь. Спасибо, кстати, за то, что мотивировали меня поставить всю ссылку вместо ссылки.
Прашант Шарма
Ответ начинается с источника: в первой строке. Обоснование для вставки копий таково: иногда внешняя ссылка на блоги устаревает, и тогда ответ становится бесполезным. И таким образом, как предложено в вете мета. Я решил скопировать все это.
Прашант Шарма
5

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

Алекс Ковентри
источник
Только что попробовал каталог с более чем 14 000 файлов. Потребовалось несколько секунд, чтобы всплыть в Emacs. Так что определенно не медленный (больше).
Беренд де Бур
3

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

(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
источник
3

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

Зак Мюррей
источник
2

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

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

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

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

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

Нарисовался
источник
2

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

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

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

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

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

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

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

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

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

Нарисовался
источник
Кто бы ни хотел объяснить, почему вы отклонили этот ответ?
Дрю
1

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

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

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

Анджей Пронобис
источник
Может helm-find-filesпометить с помощью регулярных выражений вместо M-SPC?
Нордлёв
-2

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

Бле
источник