Как я могу искать и заменять рекурсивно в каталоге в Vim?

Я узнал о команде замены Vim...

:%s/replaceme/replacement/gi

И vimgrep...

:vimgrep  /findme/gj  project/**/*.rb

Есть ли способ объединить их, чтобы выполнить замену во всех файлах в каталоге?

Ответ 1

Несмотря на то, что я пользователь Vim, я обычно использую find и sed для такого рода вещей. Синтаксис регулярного выражения для sed аналогичен Vim (они оба являются потомками ed), хотя и не идентичны. С помощью GNU sed вы также можете использовать -i для редактирования на месте (обычно sed испускает измененную версию в стандартный вывод).

Например:

find proj -name '*.rb' -type f -exec sed -i -e 's/regex/replacement/g' -- {} +

По частям:

  • proj= поиск в дереве "proj"
  • -name '*.rb'= для вещей, имена которых соответствуют '*.rb'
  • -type f= и являются обычными файлами (не каталоги, каналы, символические ссылки и т.д.).
  • -exec sed= запустить sed с этими аргументами:
    • -i= с изменениями на месте
    • -e 's/regex/replacement/g'= выполнить команду "substitute" (которая почти идентична Vim :s, но обратите внимание на отсутствие % - это значение по умолчанию в sed)
    • --= конец флагов, имена файлов начинаются здесь
    • {}= this сообщает find, что найденные имена файлов должны быть помещены в командную строку sed здесь
    • += this сообщает find, что действие -exec закончено, и мы хотим сгруппировать аргументы как можно меньше sed вызовов (как правило, более эффективно, чем запуск одного раза на имя файла). Чтобы запустить его один раз для имени файла, вы можете использовать \; вместо +.

Это общее решение с GNU sed и find. Это может быть сокращено в особых случаях. Например, если вы знаете, что ваш шаблон имени не будет соответствовать каталогам, вы можете оставить -type f. Если вы знаете, что ни один из ваших файлов не начинается с -, вы можете оставить --. Вот ответ, который я отправил на другой вопрос с более подробной информацией об отправке имен файлов, найденных с помощью find, в другие команды.

Ответ 2

args и argdo должны делать то, что вам нужно, например

:args spec/javascripts/**/*.* 
:argdo %s/foo/bar/g

Смотрите эту страницу.

Ответ 3

Вы можете запустить vimgrep, затем записать макрос, который поступает в каждую строку в выводе vimgrep, и выполняет замену, что-то вроде этого: (не вводите # комментариев!)

:set autowrite                  #automatically save the file when we change buffers
:vimgrep /pattern/ ./**/files  
qa                              #start recording macro in register a
:s/pattern/replace/g            #replace on the current line
:cnext                          #go to next matching line
q                               #stop recording
[email protected]                          #repeat the macro 10000 times, or until stopped by 
                                #an "error" such as reaching the end of the list
:set noautowrite

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

Преимущество этого над sed заключается в том, что регулярное выражение Vim более мощное, и вы можете добавить опцию c: s для проверки каждой замены.

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

Ответ 4

Там есть плагин, который пытается сделать именно это: Vimgrep Replace.

О, подождите... есть другие: Global Replace, EasyGrep.

Для решения без плагинов, возможно, argdo поможет, если вы можете получить вывод vimgrep в список аргументов (который может быть установлен с помощью args), но я не могу понять детали. Я был бы рад, если бы кто-то принял эту идею и улучшил ее... так вот, это так.

Основная идея первого плагина (я бы предположила, что другие тоже...) заключается в использовании vimgrep, а затем цикл через совпадения с :cnext и применить команду подстановки в каждой строке. Функция для выполнения этого может быть достаточно маленькой, чтобы поместить ее в .vimrc. (Может быть, вы можете поднять его из источников плагинов?)

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

Ответ 5

Одним из способов было бы открыть все файлы, которые вы хотите запустить подстановкой (или любую команду), и запустить :bufdo <cmd>, который будет запускать <cmd> во всех открытых буферах.

Ответ 6

Это работает для небольшого количества файлов.

vim `find. -type f `

: bufdo% s/pattern/replacement/gec | обновление