Ответ 1
Принцип KISS:
vim
:args `ls`
:argdo %s#SEARCH#REPLACE#gec |update
Первый символ afer% s используется как разделитель
Поиск и замена нескольких файлов затруднительно в моем редакторе. Существует множество трюков, которые можно выполнить с помощью find
, xargs
и sed
/awk
, включающих поиск и замену в нескольких файлах. Но почему-то я не мог найти способ сделать это интерактивным. Вы знаете способ сделать это?
Принцип KISS:
vim
:args `ls`
:argdo %s#SEARCH#REPLACE#gec |update
Первый символ afer% s используется как разделитель
Я бы добавил эти изменения в ответ Диллона:
Параметр -le
должен быть добавлен в команду grep
.
vim `find . -name '*.c' -exec grep -le '\<junk\>' {} \;`
Затем вы находитесь в Vim, но у вас нет возможности выбрать, что заменить, добавьте опцию c
в конце для интерактивных заметок и bufdo
в начале для прохода по каждому файлу:
:bufdo %s/junk/rubbish/gce
Позже вы сохраните всю свою работу:
:bufdo wq!
Просто используйте Vim.
Начните с использования find, чтобы составить список всех файлов, которые необходимо изменить так.
vim `find . -name '*.c' -exec grep junk {} \;`
Это запускает vim со всеми .c
файлами, содержащими строку junk
. Теперь, в vim внесите изменения в первый файл:
:%s/junk/rubbish/g
И затем введите : w Enter : n Enter для следующего файла.
Теперь вам нужно повторить процесс редактирования. Если это только одна команда подстановки и всего несколько файлов, введите : Up Enter, чтобы повторить команду подстановки.
Но если их много, вы должны использовать map
для создания нескольких макросов макроса. Первый макрос выполнил бы заменяющую команду (ы), а второй макрос выполнил команды w
и n
, чтобы сохранить изменения и получить следующий файл.
Я использовал этот (и более старые варианты с vi) в течение последних 30 лет. В частности, когда вы кодируете две части этого в виде двух макросов нажатия клавиш, вы можете запускать их очень быстро, потому что легко удерживать клавишу управления и набирать пару букв, например R Y, снова и снова. Это быстрее, чем время, необходимое для написания полностью автоматизированного script, и поскольку это просто для воспроизведения, вы можете сделать это на любой машине, на которой вы работаете.
UNIX способ сделать это с помощью grep, xargs и vi/vim:
Это поможет избежать длинных команд, когда вы хотите исключить определенные вещи из grep.
alias ge='grep --exclude=tags --exclude=TAGS --exclude-dir=.git --exclude-dir=build --exclude-dir=Build --exclude-dir=classes --exclude-dir=target--exclude-dir=Libraries --exclude=*.log --exclude=*~ --exclude=*.min.js -rOInE $*'
Команда:
ge -l 'foo' .
Команда:
ge -l 'foo' . | xargs -L 1 -o vim -c '%s/foo/bar/gc'
Объяснение команды:
Часть до трубы повторяет команду поиска
После канала мы вызываем xargs для запуска vim для каждого файла
"- L 1" сообщает xargs запускать команду в каждой строке вместо объединения всех строк в одной команде. Если вы хотите запустить vim для всех файлов одновременно, удалите "-L 1" из xargs.
"- o" говорит xargs использовать фактический терминал, а не /dev/null
У вас есть две возможности для этой команды:
a) Запустите vim для всех файлов одновременно
Преимущества:
Недостатки:
Вам нужно переключиться на каждый буфер по очереди и повторно запустить команду "% s" на нем
Если у вас много файлов, это может заставить vim потреблять много памяти (иногда столько же, сколько полноразмерная IDE!)
b) Запустите vim по очереди для каждого файла
Преимущества:
Все автоматизировано. Vim запускается и выполняет команду поиска
Свет в памяти
Недостатки:
Я предпочитаю вариант b), поскольку он автоматизирован и более UNIX-подобен. С тщательной планировкой - сначала проверьте список файлов и, возможно, запустите команду в подкаталогах, чтобы уменьшить область действия - это очень удобно использовать.
Общие преимущества:
Это может быть достигнуто с помощью стандартных инструментов UNIX, доступных практически на каждой платформе.
Приятно использовать
Общие недостатки:
Вам нужно дважды ввести выражение поиска
Вы можете столкнуться с проблемами с bash экранированием сложных выражений поиска/замены
Вот объяснение ответа @jhvaras, если вы обнаружите, что часто хотите это сделать, или хотите только искать файлы под контролем версий.
function SvnProjectSubstitute(replacement)
execute "args `grep -sl '" . @/ . "' $(svn --recursive list)`; echo -n ''"
argdo execute "substitute//" . a:replacement . "/gc"
endfunction
command -nargs=1 -complete=file Gsubstitute call SvnProjectSubstitute(<f-args>)
Примечания
Gsubstitute
предназначен для "глобальной замены". Вы можете вызвать команду, как хотите, но мне это нравится, потому что :Gs
похоже стилистически вписывается в Vim.-s
, переданный в grep
, должны скрыть ошибки, с которыми сталкиваются grep
. grep
дает ненулевой код возврата, даже если он находит хорошие результаты.Пример использования:
/OldClassName
:Gs NewClassName
Одна из причин, по которой мне нравится использовать предыдущий шаблон поиска, это позволяет вам быстро присвоить классу совершенно другое имя, поместив курсор в класс, выполнив * или #, а затем ваш :Gs
, что избавляет вас от необходимости вводить всю строку поиска.
вы можете попробовать этот инструмент для поиска и замены в интеркативном режиме внутри оболочки
https://sourceforge.net/projects/isartool/
Нет сервера X, и если вы хотите, чтобы он был рекурсивным в каталоге, используя параметр -r.
Чао
Я не мог заставить Марио отвечать на работу, но если я использую xargs, он работает.
Кроме того, в большинстве случаев, когда я хочу выполнять поиск и замену в нескольких файлах, я хочу изменить алфавитно-цифровой идентификатор от одной вещи к другой, например, переименовать переменную или класс и т.д. Для этого очень полезно добавьте отрицательный lookbehind и отрицательный lookahead, чтобы запретить символы слова до или после того, что вы заменяете, и для этого grep нужен флаг P для использования регулярного выражения Perl:
vim `find . -name '*.cpp' -o -name '*.hpp' | xargs grep -Ple '(?<!\w)FIND(?!\w)'`
:bufdo %s/FIND/REPLACE/gce
Конечно, полезно обернуть это в script, чтобы вам не приходилось повторно вводить его каждый раз. Здесь начинается:
#!/bin/bash
if [[ $# < 2 ]]; then
echo "Usage: search-replace <search> <replace>"
exit 1
fi
search="$1"
replace="$2"
files=`find . -name '*.cpp' -o -name '*.hpp' | xargs grep -Ple "(?<!\w)$search(?!\w)"`
if [[ -z "$files" ]]; then
echo "No matching .cpp or .hpp files."
exit 1
fi
# The stuff surrounding $search is the negative lookahead/lookbehind
vim -c "set hidden | bufdo %s/\(\w\)\@<!$search\(\w\)\@!/$replace/gce" $files
Что-то, что еще не работает изящно для меня с решением Марио, заключается в том, что в конце первого файла говорится:
Error detected while processing command line:
E37: No write since last change (add ! to override)
Это предотвращает переход к следующему файлу поиска и замены. Похоже, что "set hidden" исправляет это, и поэтому я использовал это в моем выше script.
Одна из оставшихся проблем заключается в том, что после выполнения поиска и замены в первом файле он, кажется, обрабатывает этот первый файл во второй раз, отменяя ваши первоначальные замены. Если кто-нибудь знает, почему это происходит, дайте мне знать.