Поиск и замена в Vim во всех файлах проекта


117

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

Один из способов сделать это - просто открыть все файлы в текущем каталоге:

:args ./**

а затем выполните поиск и замените все открытые файлы:

:argdo %s/Search/Replace/gce

Однако, когда я это делаю, использование памяти Vim подскакивает с пары десятков МБ до более 2 ГБ, что для меня не работает.

У меня также установлен плагин EasyGrep , но он почти никогда не работает - либо не находит все вхождения, либо просто зависает, пока я не нажму CtrlC. Пока что мой предпочтительный способ выполнить эту задачу - это ack-grep для поискового запроса, с помощью окна быстрого исправления открыть любой файл, который содержит термин и не был открыт раньше, и, наконец :bufdo %s/Search/Replace/gce.

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


1
@Cascabel Поскольку вы написали этот комментарий, существует сайт vi.stackexchange.com.
— Flimm

Ответы:


27

Greplace мне подходит .

Также на гитхабе есть готовая версия для патогенов .


8
Установил его, другие команды вроде :Gsearchи :Gbuffersearchсуществуют, но когда набираю, :Greplaceполучаю Not an editor command: Greplace.
— Ramon Tayag 02

согласно документации, синтаксис: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] так что вы можете выполнить рекурсивный поиск, используя, например::Gsearch -r pattern dir/
— vitorbal

Что я ненавижу в Greplace, так это то, что если шаблон находится в имени файла, Greplace не работает, потому что вы в конечном итоге заменяете его и в имени файла.
— Дэниел

2
Ничего необычного здесь через 6 лет, но этот плагин сильно устарел по сравнению с некоторыми новыми ответами ( github.com/yegappan/greplace ), последним обновлением 3+ лет назад. Я мог бы проверить встроенное решение Сида: stackoverflow.com/a/38004355/2224331 (Это не я в другом аккаунте, просто совпадение: P)
— SidOfc

99

Другой важный вариант - просто не использовать vim:

sed -i 's/pattern/replacement/' <files>

или, если у вас есть способ создать список файлов, возможно, что-то вроде этого:

find . -name *.cpp | xargs sed -i 's/pattern/replacement/'
grep -rl 'pattern1' | xargs sed -i 's/pattern2/replacement/'

и так далее!


Спасибо! Мне действительно нужно изучить эту старую школьную командную строку. Думаю, это лучший ответ. Это регулярные выражения: регулярные выражения Perl, регулярные выражения vim или какой-либо другой вид регулярных выражений?
— Эрик Джонсон

2
@EricJohnson: Это ... sedрегулярные выражения, которые, по сути, являются базовыми регулярными выражениями POSIX.2, но не совсем (это находится на странице руководства) - они позволяют \nсопоставлять символы новой строки и некоторые другие подобные вещи. Поэтому они почти такие же, как grepрегулярные выражения.
— Cascabel

Есть ли причина, по которой у вас есть -i в команде sed? На странице руководства говорится, что это для указания расширения, но вы, похоже, не указываете его.
— davekaro

Хорошо, похоже, что «s / pattern / replacement /» - это расширение, теперь я думаю, что я понял.
— davekaro

11
В Mac OS X единственная команда sed, которая сработала для меня, была:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
— ghodss

74

РЕДАКТИРОВАТЬ: используйте cfdoкоманду вместо, cdoчтобы значительно уменьшить количество команд, которые будут выполняться для этого (потому что cdoзапускает команды для каждого элемента, а cfdoвыполняет команды для каждого файла)

Благодаря недавно добавленной cdoкоманде , теперь вы можете сделать это двумя простыми командами, используя любой grepустановленный вами инструмент. Никаких дополнительных плагинов не требуется !:

1. :grep <search term>
2. :cdo %s/<search term>/<replace term>/gc
3. (If you want to save the changes in all files) :cdo update

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

(Удалите знак cв конце второй команды, если вы хотите заменить каждый поисковый запрос без подтверждения каждый раз)


12
Плюс вы можете добавить, :cdo updateчтобы сохранить изменения во всех файлах.
— Чжэ Ли

1
Но он будет делать паузу, чтобы каждый раз предупреждать о том, что не сохраняется, перед переключением на следующий буфер, когда текущий буфер изменяется.
— lfree

1
@Zhe спасибо, я добавил это к ответу; @lfree Я лично предпочитаю подтверждать каждое изменение, но вы можете удалить знак cв конце второй команды (просто используйте g), чтобы она
— Сид,

1
: cdo% s / search term / replace term / g заменяет только первое вхождение, но не работает для второго вхождения в моем vim
— sunxd

3
для использования updateя должен был:cdo %s/<search term>/<replace term>/g | update
— gabe

34

Я решил использовать ack и Perl для решения этой проблемы, чтобы воспользоваться преимуществами более мощных полных регулярных выражений Perl, а не подмножества GNU.

ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'

объяснение

извед

извед удивительный инструмент командной строки , которая представляет собой смесь grep, findи полные Perl регулярные выражения (не только GNU субпопуляции). Он написан на чистом Perl, он быстрый, имеет подсветку синтаксиса, работает в Windows и более удобен для программистов, чем традиционные инструменты командной строки. Установите его в Ubuntu с помощью sudo apt-get install ack-grep.

xargs

Xargs - это старый инструмент командной строки unix. Он считывает элементы из стандартного ввода и выполняет указанную команду, за которой следуют элементы, считанные для стандартного ввода. Таким образом, в основном список файлов, сгенерированных ack, добавляется в конец perl -pi -E 's/pattern/replacemnt/g'команды.

perl -pi

Perl - это язык программирования. Параметр -p заставляет Perl создавать цикл вокруг вашей программы, который перебирает аргументы имени файла. Параметр -i заставляет Perl редактировать файл на месте. Вы можете изменить это для создания резервных копий. Параметр -E заставляет Perl выполнять одну строку кода, указанную как программу. В нашем случае программа представляет собой замену регулярного выражения Perl. Для получения дополнительной информации о параметрах командной строки Perlperldoc perlrun . Для получения дополнительной информации о Perl см. Http://www.perl.org/ .


Мне нравится этот ответ, потому что он работает даже с окончанием строки cygwin. Обратите внимание, что он добавляет \ r в конец измененных строк, но при использовании sed он заменяет каждую новую строку, делая ваши различия непригодными. Perl немного более разумный, и это действительно отличный однострочный инструмент для поиска и замены. Спасибо!
— andrew

@andrew, я не совсем уверен, что в твоем случае не так. Помогло бы вам использовать \ R вместо \ n в ваших регулярных выражениях? См. Раздел \ R в perldoc.perl.org/perlrebackslash.html#Misc . Также попробуйте perldoc.perl.org/perlre.html#Regular-Expressions . Perl чрезвычайно кроссплатформенный, и я уверен, что у вас есть способ не получить искаженные окончания строк.
— Эрик Джонсон

В моем регулярном выражении нет новых строк, версии этих программ для cygwin автоматически добавляют \ r в конце каждой измененной строки. Мне особенно понравилась версия perl, потому что она изменяет только совпадающие строки, тогда как sed изменяет все строки, даже если они не соответствуют регулярному выражению.
— Andrew

Что делать, если путь к файлу содержит пробелы?
— shengy 06

23

может сделать это:

:noautocmd vim /Search/ **/*
:set hidden
:cfirst
qa
:%s//Replace/gce
:cnf
q
1000@a
:wa

Объяснение:

  • :noautocmd vim /Search/ **/*⇒ поиск ( vimэто сокращение от vimgrep) шаблона во всех файлах во всех подкаталогах cwd без запуска autocmds ( :noautocmd) для повышения скорости.
  • :set hidden ⇒ разрешить модифицированные буферы, не отображаемые в окне (может быть в вашем vimrc)
  • :cfirst ⇒ перейти к первому результату поиска
  • qa ⇒ начать запись макроса в регистр a
  • :%s//Replace/gce⇒ заменить все вхождения последнего поискового шаблона (еще /Search/в то время) на Replace:
    • несколько раз на одной строке ( gфлаг)
    • с подтверждением пользователя ( cфлаг)
    • без ошибки, если шаблон не найден ( eфлаг)
  • :cnf⇒ перейти к следующему файлу в списке, созданном vimкомандой
  • q ⇒ остановить запись макроса
  • 1000@a ⇒ воспроизвести макрос, сохраненный в регистре, 1000 раз
  • :wa ⇒ сохранить все измененные буферы

* РЕДАКТИРОВАТЬ * Vim 8 способ:

Начиная с Vim 8 есть способ сделать это лучше, поскольку он :cfdoвыполняет итерацию по всем файлам в списке быстрых исправлений:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa

Возможно, вы захотите подробнее объяснить это для нас, простых смертных. Мне непонятно, нужно ли каждый раз выполнять эту «последовательность». Я делаю это быстрее, используя свой старый ржавый Delphi 5, так что наверняка мне что-то не хватает.
— Ливен Кеерсмэкерс

1
@Lieven: Вы правы, это непонятно без комментариев. Я разработаю некоторые объяснения.
— Бенуа

+1 но, хотя vimменя во многом завоевали, я думаю, что буду придерживаться PowerGrep с этим.
— Ливен Кеерсмэкерс

Спасибо за ваш ответ и объяснение всех команд, я очень ценю это. Я принимаю другой ответ, потому что предпочитаю использовать для этого плагин.
— psyho

Я отметил это, потому что это вызывает больше путаницы, учитывая несоответствие между уровнем человека, задающего вопрос, и уровнем, необходимым для понимания этого ответа.
— Ллойд Мур

22

Populate :args из команды оболочки

Возможно (в некоторых операционных системах 1 )) предоставить файлы для:args помощью команды оболочки.

Например, если у вас установлен ack 2 ,

:args `ack -l pattern`

попросит ack вернуть список файлов, содержащих «шаблон», и поместить их в список аргументов.

Или с помощью простого grep, я думаю, это будет:

:args `grep -lr pattern .`  


Затем вы можете просто использовать, :argdoкак описано в OP:

:argdo %s/pattern/replacement/gce


Заполняем :argsиз списка QuickFix

Также ознакомьтесь с ответом nelstrom на связанный вопрос, описывающий простую пользовательскую команду, которая заполняет список arglist из текущего списка quickfix. Это отлично работает со многими командами и плагинами, вывод которых попадает в список quickfix ( :vimgrep, :Ack3 , :Ggrep4 ).

Последовательность выполнения широкого поиска по проекту может быть выполнена с помощью:

:vimgrep /pattern/ **/*
:Qargs 
:argdo %s/findme/replacement/gc

где :Qargs- вызов пользовательской команды, заполняющей список аргументов из списка быстрых исправлений.

Вы также найдете ссылки в последующем обсуждении на простые плагины, которые сокращают этот рабочий процесс до 2 или 3 команд.

связи

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. беглец - github.com/tpope/vim-fugitive

1
argdo останавливается после первого файла с проблемой записи
— frankster 03


5

Если вы не против введения внешней зависимости, я приготовил плагин ctrlsf.vim (зависит от ack или ag ) для выполнения этой работы.

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

Возможно, следующая демонстрация объяснит больше

ctrlsf_edit_demo


3
1. :grep <search term> (or whatever you use to populate the quickfix window)
2. :cfdo %s/<search term>/<replace term>/g | update

Шаг 1 заполняет список быстрых исправлений нужными элементами. В этом случае он распространяется с поисковыми запросами, которые вы хотите изменить через grep.

cfdoвыполняет следующую команду для каждого файла в списке быстрых исправлений. Введите :help cfdoподробности.

s/<search term>/<replace term>/gзаменяет каждый термин. /gозначает заменять каждое вхождение в файле.

| update сохраняет файл после каждой замены.

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


Для меня в NeoVim нужно было сделать небольшую модификацию в строке 2 выше: 2. :cfdo %s/<search term>/<replace term>/g | updateбез символа заменяется %только первое вхождение шаблона.
— Карим Эргави

Спасибо Карим. Я обновил ответ, так как %sработает и на обычном vim.
— Кайл Хейрономус

0

По сути, я хотел заменить в одной команде и, что более важно, в самом vim

Основываясь на ответе @Jefromi, я создал сочетание клавиш, которое я установил в моем файле .vimrc следующим образом

nmap <leader>r :!grep -r -l  * \| xargs sed -i -e 's///g'

теперь из vim, по штриху лидера + r я загружаю команду в vim, которую я редактирую, как показано ниже,

:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'

Следовательно, я выполняю замену с помощью одной команды и, что более важно, в самом vim


и я на самом деле использую ag вместо grep
— deepak
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.