Використання Emacs для рекурсивного пошуку та заміни текстових файлів, які вже не відкриті


212

Надаючи це питання , я намагаюся з'ясувати, як зробити щось подібне, що повинно бути простим, що особливо не дозволяє мені звикнути до використання Emacs і замість цього запустити редактор, з яким я вже знайомий. Я тут досить часто використовую приклад для редагування кількох файлів.

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

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

Відповіді:


370
  1. M-x find-name-dired: вам буде запропоновано ввести кореневу директорію та шаблон імені файлу.
  2. Натисніть, tщоб "переключити позначку" для всіх знайдених файлів.
  3. Натисніть Qдля "Замінити запит у файлах ...": вам буде запропоновано повторити запит / заміну.
  4. Поступайте так query-replace-regexp: SPACEяк замінити та перейти до наступного матчу, nпропустити матч тощо.
  5. Натисніть, C-x sщоб зберегти буфери. (Ви можете натиснути y, nабо !зберегти всі відразу)

10
Ні, це рекурсивно. Нерекурсивна версія буде в mred-режимі% m.
Кріс Конвей

5
Можливо тому, що ви телефонуєте C: \ WINDOWS \ system32 \ find.exe, а не GNU find.
Джаз

6
Стін, Кріс: Мабуть, не зовсім те, що ви хочете, але ви можете використовувати "Cx s" (save-some-buffers), це підкаже вам для кожного зміненого файлу, тому вам потрібно лише натиснути "y" кілька разів.
danielpoe

48
C-x s !щоб зберегти всі файли одразу.
cYrus

10
M-,для продовження пошуку та заміни (наприклад, після запиту "повернути файл", якщо файл змінився на диску).
tfischbach

37
  • M-x find-name-dired RET
    • може знадобитися деякий час, щоб усі файли з’явились у списку, прокрутіть донизу ( M->), поки не з'явиться " find finished", щоб переконатися, що всі вони завантажені
  • Натисніть tдля "переключення позначки" для всіх знайдених файлів
  • Натисніть Qдля "Замінити запит у файлах ...": вам буде запропоновано повторити запит / заміну.
  • Поступайте так, як із запитом-заміна-повторне вираження: 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, це дуже зручний інструмент, я настійно рекомендую вам переглянути його.


Ця методика досить потужна навіть без використання iedit. Це перетворює знання грепу (легко з lgrep / rgrep) у знання того, як обширно шукати-замінювати!
NeilenMarais

Поєднання rgrep / wgrep / macros є приголомшливим.
окудо

2
Прив'язка клавіш до початку wgrep- C-c C-pдопит.
techniao

15

Снаряд дійсно приємний: Cc pr запускає команду снаряд-заміна


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 finder-query-substitute RET Collectible RET <the substitution> RET. * \. java RET <каталог для початку пошуку в> RET
ShreevatsaR

Мені подобається такий підхід (він дозволяє уникнути всіх невмілих причетностей до прямого підходу). Я використовую деякі функції зручності, щоб поєднати це зі снарядом (проект-корінь-виявити) і замінити річ у точці: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att

6

Джерело інформації: 1

Для користувачів emacs pro:

  1. Викличте перенаправлені, щоб перелічити файли в dir, або зателефонуйте, якщо знайти всі підкаталоги.
  2. Позначте потрібні файли. Ви можете позначити повторним виразом, ввівши 【% m】.
  3. Введіть Q для виклику dired-do-query-substitu-regexp.
  4. Введіть свій регекс пошуку та замініть рядок. 〔☛ загальна схема виразного еліпса〕
  5. У кожному випадку введіть y для заміни, n для пропуску. Наберіть 【Ctrl + g】, щоб перервати всю операцію.
  6. Тип! щоб замінити всі події в поточному файлі без запитання, N, щоб пропустити всю можливу заміну на решту поточного файлу. (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 $. (Ви можете знайти список команд для позначення у графічному меню "Позначити" (це меню з'являється, коли ви перебуваєте в переведеному режимі).)

Вибір файлів у каталозі та всіх його підкаталогах

Якщо ви хочете знайти / замінити файли всередині каталогу, включаючи сотні підкаталогів, ось метод вибору всіх цих файлів.

Зателефонуйте, знайшовшись. (Ви викликаєте команду, натискаючи 【Alt + x】) Потім введіть ім'я каталогу, ⁖ / Користувачі / mary / myfiles

Примітка: якщо ви використовуєте emacs на не графічному текстовому терміналі unix, і якщо 【Alt + x】 не працює, еквівалентний штрих ключа є is Esc x】.

Emacs запитає вас із запитом "Виконати пошук (з аргументами):". Якщо вам потрібно зробити заміну на всіх HTML-файлах, введіть -name "* html". Якщо вам не байдуже, який тип файлу, а просто всі файли в цьому режимі, то дайте "-типу f"

Тепер позначте файли, як описано вище.

Інтерактивний пошук / заміна

Тепер ви готові замінити інтерактивну пошук. Для простоти скажімо, що ви просто хочете замінити слово "швидкий" на "супер". Тепер зателефонуйте dired-do-query-substitu-regexp. Він запропонує вам ввести рядок regex та рядок заміни. Введіть "швидкий", введіть, а потім "супер".

Тепер, emacs буде використовувати ваш шаблон і перевіряти файли, а також зупинятись та показувати вам, коли відбудеться збіг. Коли це станеться, emacs підкаже вам, і у вас є вибір внести зміни або пропустити зміни. Щоб внести зміни, введіть y. Щоб пропустити, введіть n. Якщо ви просто хочете, щоб Emacs продовжував і вносив усі такі зміни в поточний файл, введіть !.

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

Збереження змінених файлів

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

Якщо ви використовуєте emacs версії 22 або новішої версії, то зателефонуйте ibuffer, щоб перейти в режим переліку буфера, а потім наберіть 【* u】, щоб позначити всі збережені файли, а потім введіть S, щоб зберегти їх усі. (це зрушення)

Якщо ви використовуєте emacs версії 21, ви можете зробити це: буфери списку викликів, а потім перемістити курсор у файл, який ви хочете зберегти, і введіть s. Він позначить файл для подальшої дії збереження. Введіть u, щоб зняти позначку. Після завершення введіть x, щоб виконати збереження всіх файлів, позначених для збереження. (в emacs відкритий файл називається "буфер". Ігноруйте інші речі там.)

Альтернативно вищевказаним параметрам ви також можете зателефонувати save-some-buffers 【Ctrl + xs】. Потім emacs покаже кожен збережений файл і запитає, чи хочете ви його зберегти.

Примітка: регулярний вираз emacs не такий, як Perl чи Python, але подібний. Для підсумків та загальних моделей див.: Emacs Regex.


2
Ця відповідь повинна зробити його більш привітним, ніж найбільш проголосовану відповідь, на яку я сподіваюся. Дякую до речі за те, що я мотивував мене поставити всю справу замість посилання.
Прашант Шарма

Відповідь починається з джерела: як перший рядок. Обґрунтуванням вставлення копій є: іноді зовнішнє посилання на блоги застаріває, і тоді відповідь буде марною. І таким чином, як пропонує мета wiki. Я вирішив скопіювати всю справу.
Прашант Шарма

5

Використання dired для повторного запису в глибоке дерево каталогів буде для цього завдання трохи повільним. Ви можете розглянути можливість використання тегів-запитів-заміни . Це означає обстріл для створення таблиці тегів, але це все одно корисно, і це швидко.


Щойно приміряв каталог із понад 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)))))

3

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


2

Інший варіант - використовувати пошук Icicles . Це інший вид покрокового пошуку, який використовує завершення введення мінібуфера проти пошукових запитів. Коли ви змінюєте поточний ввід, набір відповідних звернень оновлюється в буфері*Completions* .

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

Під час відвідування пошукового запиту ви можете на вимогу замінити цілий хіт або лише ту частину, яка відповідає вашому поточному вводу мінібуфера. Заміна на вимогу означає, що ви не запитуєте про кожне звернення пошуку по черзі; Ви отримуєте доступ до потрібних звернень безпосередньо в будь-якому порядку. Цей підхід може бути ефективнішим, ніж замінити запит, якщо у вас є обмежена кількість заміни: ви пропускаєте вичерпніy/n .

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

Ви також можете сортувати пошукові запити в різних порядках сортування для легшого доступу / навігації.


2

find-name-dired гаразд, але:

  • Усі файли, які ви отримуєте, відповідають одній і тій же одноразовій повторній передачі.
  • find-diredє більш гнучким у цьому плані, але він також створений для використання загальних правил (навіть якщо вони можуть бути довільно складними). І звичайно findє своя, складна мова.
  • якщо потім ви хочете діяти лише на деяких файлах, імена яких були зібрані в find(-name)-diredбуфері, вам потрібно або позначити їх, або видалити / опустити рядки тих, на яких ви не хочете діяти.

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

Наприклад, M-+ Qте саме, що Q--- замінити запит, але цільовими файлами є всі ті, що позначені в поточному режимі та в будь-яких позначених підкаталогах, вниз, вниз, вниз ...

Так, альтернативою використанню таких команд «тут і нижче» є вставлення всіх підкаталогів та їх підкаталів, рекурсивно, а потім використання команди верхнього рівня, наприклад Q. Але часто може бути зручно не турбуватися із вставленими підкатами.

А для цього вам все одно потрібен швидкий спосіб рекурсивно вставляти всі такі підкаталоги . Тут теж може допомогти Dired + . M-+ M-iвставляє всі позначені підкаталоги та власні позначені підкаталоги, рекурсивно. Тобто він схожий M-i(який вставляє позначені підкаталоги в Dired + ), але він діє рекурсивно на підкаталоги.

(Усі такі команди "тут і нижче" " Dired +" знаходяться в меню Кілька > Позначено тут і нижче .)

Ви також можете виконувати подвійні операції на наборі файлів Emacs , який є збереженим набором імен файлів, розташованих у будь-якому місці. І якщо ви використовуєте Icicles, то ви можете відкрити Dired буфер лише для файлів у наборі файлів або інших типів збережених списків файлів.

Ви також можете зробити закладку будь-якого буфера Dired, включаючи той, який ви створили за допомогою find(-name)-dired. Це дає вам швидкий спосіб повернутися до такого набору (наприклад, набору проектів) пізніше. І якщо ви використовуєте Bookmark +, то закладка записів на Dired буфер (a) його lsкомутатори, (b) які файли позначені, (c) які підкаталоги вставляються та (d) які (під) каталоги приховані. Все це відновлюється, коли ви "переходите" до закладки. Закладка + також дозволяє вам розміщувати закладки на цілому дереві в'язаних буферів --- перехід до закладок відновлює всі буфери на дереві.


Хто завгодно: Хочете пояснити, чому ви спростували цю відповідь?
Дрю

1

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

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

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


Можна helm-find-filesпозначити за допомогою regexp замість M-SPC?
Nordlöw

-2

Це не Emacs, але xxdiff поставляється з інструментом під назвою xx-перейменувати, який буде робити це для декількох рядків одночасно (наприклад, від до до ВІД), за допомогою інтерактивного підказки, зберігайте резервні копії всіх змінених файлів і створюйте короткий журнал змін, внесених із контекстом. Це те, що я, як правило, використовую, коли роблю великі / глобальні перейменування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.