Как я могу найти и заменить рекурсивно в каталоге в Vim? - PullRequest
39 голосов
/ 22 января 2010

Я узнал о замещающей команде Вима ...

:%s/replaceme/replacement/gi

И вимгреп ...

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

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

Ответы [ 6 ]

64 голосов
/ 22 января 2010

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

Например:

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

По частям:

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

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

54 голосов
/ 27 сентября 2012

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

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

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

7 голосов
/ 23 января 2010

Вы можете запустить 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
10000@a                          #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, чтобы проверить каждую замену.

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

5 голосов
/ 22 января 2010

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

4 голосов
/ 22 января 2010

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

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

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

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

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

0 голосов
/ 19 сентября 2014

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

vim `найди. тип f `

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...