Интерактивный поиск и замена из оболочки - PullRequest
8 голосов
/ 28 июня 2011

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

Ответы [ 8 ]

6 голосов
/ 22 ноября 2012

Принцип поцелуя:

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

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

3 голосов
/ 11 сентября 2012

Я бы добавил эти модификации в ответ Диллона:

В команду grep должна быть добавлена ​​опция -le.

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

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

:bufdo %s/junk/rubbish/gce

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

:bufdo wq!
1 голос
/ 09 августа 2017

UNIX способ сделать это с помощью grep, xargs и vi / vim:

  1. сделать псевдоним

Это поможет избежать длинных команд, если вы хотите исключить определенные вещи из 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 открыть следующий

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

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

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

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

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

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

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

1 голос
/ 09 июля 2011

Просто используйте Vim.

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

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

Запускает vim со всеми файлами .c, содержащимистрока junk.Теперь в vim внесите изменения в первый файл:

:%s/junk/rubbish/g

, а затем введите : w Введите : n Введите для следующего файла.

Теперь вам нужно повторить процесс редактирования.Если это только одна команда замены и всего несколько файлов, введите : Up Введите , чтобы повторить команду замены.

Но еслиих много, вы должны использовать map для создания пары макросов kestroke.Первый макрос будет выполнять команду (команды) замены, а второй макрос будет выполнять команды w и n, чтобы сохранить изменения и получить следующий файл.

Я использовал это (и более старые варианты сvi) за последние 30 лет.В частности, когда вы кодируете две части этого как два макроса нажатия клавиш, вы можете запустить его очень быстро, потому что легко удерживать нажатой клавишу управления и набирать пару букв, например, RY, снова и снова.Это быстрее, чем время, необходимое для написания полностью автоматизированного сценария, и, поскольку его легко воспроизвести, вы можете сделать это на любом компьютере, на котором вы работаете.

0 голосов
/ 22 марта 2019

Из ответа jhvaras я создал команду bash для быстрого поиска и замены (для добавления к .bashrc):

replace () {
    if [ $# -lt 2 ]
    then
        echo "Recursive, interactive text replacement"
        echo "Usage: replace text replacement"
        return
    fi

    vim -u NONE -c ":execute ':argdo %s/$1/$2/gc | update' | :q" $(ag $1 -l)
}

Используется следующим образом:

~$ replace some_text some_new_text

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

0 голосов
/ 04 августа 2016

Я не смог получить ответ Марио на работу, но если я использую xargs, он работает.

Кроме того, в большинстве случаев, когда я хочу выполнять поиск и замену в нескольких файлах, я хочу изменить буквенно-цифровой идентификаторот одного к другому, например, переименование переменной или класса и т. д. Для этого очень полезно добавить отрицательный взгляд назад и отрицательный взгляд вперед, чтобы запретить символы слова до или после того, что вы заменяете, и для этого grep требуетсяФлаг P для использования регулярного выражения Perl:

vim `find . -name '*.cpp' -o -name '*.hpp' | xargs grep -Ple '(?<!\w)FIND(?!\w)'`

:bufdo %s/FIND/REPLACE/gce

Конечно, полезно обернуть это в сценарий, чтобы вам не приходилось каждый раз вводить его заново.Вот начало:

#!/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» исправляет это, и поэтому я использовал это в своем вышеупомянутом скрипте.

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

0 голосов
/ 23 ноября 2013

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

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

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

Чао

0 голосов
/ 19 декабря 2012

Это расширение ответа @ 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 для поиска их всех (то есть это, вероятно, решит проблему этого парня тоже).
  • эхо бездействия в конце и -s, переданные grep,чтобы скрыть ошибки, которые grep встречает.grep дает ненулевой код возврата, даже если он находит хорошие результаты.

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

/OldClassName
:Gs NewClassName

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

...