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

117

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

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

:args ./**

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

:argdo %s/Search/Replace/gce

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

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

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

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

Ответы:

27

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

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

tuxcanfly
источник
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/'

и так далее!

Cascabel
источник
Спасибо! Мне действительно нужно изучить эту старую школьную командную строку. Думаю, это лучший ответ. Это регулярные выражения: регулярные выражения 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в конце второй команды, если вы хотите заменить каждый поисковый запрос без подтверждения каждый раз)

Sid
источник
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
Benoit
источник
Возможно, вы захотите подробнее объяснить это для нас, простых смертных. Мне непонятно, нужно ли каждый раз выполнять эту «последовательность». Я делаю это быстрее, используя свой старый ржавый 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
exbinary
источник
1
argdo останавливается после первого файла с проблемой записи
frankster 03
9

Еще один вариант в 2016 году, плагин far.vim :

far.vom

Олег Халидов
источник
4
Вы также должны сказать, что являетесь автором плагина :)
sitilge
5

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

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

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

ctrlsf_edit_demo

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