Интерактивный поиск и замена оболочки

Поиск и замена нескольких файлов затруднительно в моем редакторе. Существует множество трюков, которые можно выполнить с помощью find, xargs и sed/awk, включающих поиск и замену в нескольких файлах. Но почему-то я не мог найти способ сделать это интерактивным. Вы знаете способ сделать это?

Ответ 1

Принцип KISS:

vim
:args `ls`
:argdo %s#SEARCH#REPLACE#gec |update

Первый символ afer% s используется как разделитель

Ответ 2

Я бы добавил эти изменения в ответ Диллона:

Параметр -le должен быть добавлен в команду grep.

vim `find . -name '*.c' -exec grep -le '\<junk\>'  {} \;`

Затем вы находитесь в Vim, но у вас нет возможности выбрать, что заменить, добавьте опцию c в конце для интерактивных заметок и bufdo в начале для прохода по каждому файлу:

:bufdo %s/junk/rubbish/gce

Позже вы сохраните всю свою работу:

:bufdo wq!

Ответ 3

Просто используйте 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, и поскольку это просто для воспроизведения, вы можете сделать это на любой машине, на которой вы работаете.

Ответ 4

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 $*'
  1. Выполните поиск и проверьте список файлов

Команда:

ge -l 'foo' .
  1. Повторите поиск, указав vim, чтобы он в интерактивном режиме заменил каждый файл

Команда:

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 для всех файлов одновременно

Преимущества:

  • Команда легко отменить: просто закройте vim

Недостатки:

  • Вам нужно переключиться на каждый буфер по очереди и повторно запустить команду "% s" на нем

  • Если у вас много файлов, это может заставить vim потреблять много памяти (иногда столько же, сколько полноразмерная IDE!)

b) Запустите vim по очереди для каждого файла

Преимущества:

  • Все автоматизировано. Vim запускается и выполняет команду поиска

  • Свет в памяти

Недостатки:

  • Трудно отменить: выход из vim просто заставит xargs открыть следующий

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

Общие преимущества:

  • Это может быть достигнуто с помощью стандартных инструментов UNIX, доступных практически на каждой платформе.

  • Приятно использовать

Общие недостатки:

  • Вам нужно дважды ввести выражение поиска

  • Вы можете столкнуться с проблемами с bash экранированием сложных выражений поиска/замены

Ответ 5

Вот объяснение ответа @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>)

Примечания

  • Как я сформулировал здесь, он ищет только те файлы, которые Subversion имеет под управлением версии из текущего каталога, рекурсивного вниз. Вы можете отказаться от курса, чтобы обобщить это или поместить его в другую систему управления версиями в соответствии с вашими потребностями.
  • Используемый шаблон - это последний шаблон, который был найден. Еще раз, если вам это не нравится, вы можете поместить его под свои нужды.
  • Gsubstitute предназначен для "глобальной замены". Вы можете вызвать команду, как хотите, но мне это нравится, потому что :Gs похоже стилистически вписывается в Vim.
  • Это приятное решение, если у вас очень большое количество файлов, потому что он берет на себя ответственность за Vim для поиска через них всех (т.е. он, вероятно, решит эту проблему парня).
  • Отклик do-nothing в конце и -s, переданный в grep, должны скрыть ошибки, с которыми сталкиваются grep. grep дает ненулевой код возврата, даже если он находит хорошие результаты.

Пример использования:

/OldClassName
:Gs NewClassName

Одна из причин, по которой мне нравится использовать предыдущий шаблон поиска, это позволяет вам быстро присвоить классу совершенно другое имя, поместив курсор в класс, выполнив * или #, а затем ваш :Gs, что избавляет вас от необходимости вводить всю строку поиска.

Ответ 6

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

https://sourceforge.net/projects/isartool/

Нет сервера X, и если вы хотите, чтобы он был рекурсивным в каталоге, используя параметр -r.

Чао

Ответ 7

Я не мог заставить Марио отвечать на работу, но если я использую 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.

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