Шукайте та замінюйте у 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 добре працює для мене.

На Github також є готова до збудника версія .


8
Встановивши це, я бачу, що інші команди люблять :Gsearchі :Gbuffersearchіснують, але коли я набираю, :Greplaceя отримую Not an editor command: Greplace.
Рамон Таяг

згідно з документами, синтаксис такий: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] щоб ви могли здійснити рекурсивний пошук, використовуючи, наприклад::Gsearch -r pattern dir/
vitorbal

Те, що я ненавиджу у Грепласа, - це те, що якщо шаблон є у імені файлу, Греплас виходить з ладу, тому що ви в кінцевому підсумку замінюєте його і на ім'я файлу.
Даніель

2
Нічого незвичайного тут немає через 6 років, але цей плагін сильно застарів у порівнянні з деякими новими відповідями ( github.com/yegappan/greplace ) останнім оновленням 3+ років тому. Я можу ознайомитись із вбудованим рішенням від Sid: 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/'

і так далі!


Дякую! Мені справді потрібно вивчити цей матеріал із командного рядка старої школи Я думаю, що це найкраща відповідь. Це регулярні регекси, регекси, або vim, або якісь інші регекси?
Ерік Джонсон

2
@EricJohnson: Це ... sedрегулярні вирази, які по суті є базовими регулярними виразами POSIX.2, але не зовсім так (це на сторінці сторінки) - вони дозволяють \nвідповідати новим рядкам та деяким подібним речам. Тому вони майже однакові як grepрегулярні вирази.
Каскабель

Чи є у вас причина -i в команді sed? На сторінці "man" йдеться про те, щоб вказати розширення, але, здається, його фактично не вказати.
davekaro

Гаразд, це насправді виглядає як "s / pattern / заміна /" - це розширення, я думаю, зараз розумію.
davekaro

11
На Mac OS X єдиною командою sed, яка працювала на мене, була:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss

74

EDIT: Використовуйте 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 / пошуковий термін / замінити термін / 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 виконувати один рядок коду, вказаний як програма. У нашому випадку програма - це лише заміна Perge regex. Для отримання додаткової інформації про параметри командного рядка Perl perldoc perlrun. Для отримання додаткової інформації про Perl див. Http://www.perl.org/ .


Мені подобається ця відповідь, тому що вона працює навіть із закінченнями лінії cygwin. Зауважте, що він додає \ r до кінця модифікованих рядків, але при використанні sed він замінить кожен новий рядок, зробивши ваш diff непридатним. 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

Що робити, якщо шлях до файлу містить пробіли?
Шенгі

23

можливо, зробіть це:

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

Пояснення:

  • :noautocmd vim /Search/ **/*Pattern пошук шаблону ( 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 ⇒ зберегти всі модифіковані буфери

* EDIT * Vim 8 спосіб:

Починаючи з Vim 8, є кращий спосіб зробити це, оскільки він :cfdoповторюється для всіх файлів у списку швидких виправлень:

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

ви можете пояснити, що це трохи далі для нас, менших смертних. Мені не зрозуміло, чи потрібно робити цю «послідовність» кожен раз. Я швидше роблю це, використовуючи мій іржавий старий Delphi 5, так що, безумовно, мені щось не вистачає.
Лівен Кірсмейкерс

1
@Lieven: Ви маєте рацію, це трохи незрозуміло без коментарів. Я розробимо якесь пояснення.
Бенуа

+1, але, хоча vimперемагає мене за багато речей, я думаю, я буду дотримуватися PowerGrep з цим.
Лівен Кірсмейкер

Дякую за вашу відповідь та пояснення всіх команд, я дуже ціную це. Я приймаю іншу відповідь, тому що для цього я віддаю перевагу використовувати плагін.
psyho

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

22

Популяція :argsз команди оболонки

Можна (на деяких операційних системах 1 ) подавати файли за :argsдопомогою команди оболонки.

Наприклад, якщо у вас встановлено Ack 2 ,

:args `ack -l pattern`

попросить ack повернути список файлів, що містять 'pattern', і помістить їх у список аргументів.

Або з простим ol 'grep я думаю, це було б:

:args `grep -lr pattern .`  


Потім можна просто використовувати, :argdoяк описано в ОП:

:argdo %s/pattern/replacement/gce


Населення :argsзі списку швидких виправлень

Також ознайомтеся з відповіддю nelstrom на пов’язане запитання, що описує просту команду, визначену користувачем, яка заповнює список аргументів із поточного списку швидких виправлень. Це чудово працює з багатьма командами та плагінами, вихід яких опиняється у списку швидких виправлень ( :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 - bestthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. втікач - github.com/tpope/vim-fugitive

1
argdo зупиняється після першого файлу з проблемою запису
frankster


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. :cfdo %s/<search term>/<replace term>/g | updateБез цього %, замінено лише перше виникнення шаблону.
Kareem Ergawy

Дякую Карім. Я оновив відповідь, оскільки %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.