Wyszukaj i zamień w Vimie we wszystkich plikach projektu


117

Szukam najlepszego sposobu na przeszukiwanie i zastępowanie (z potwierdzeniem) we wszystkich plikach projektu w Vimie. Przez „pliki projektu” rozumiem pliki w bieżącym katalogu, z których niektóre nie muszą być otwarte.

Jednym ze sposobów na to może być po prostu otwarcie wszystkich plików w bieżącym katalogu:

:args ./**

a następnie wyszukaj i zamień na wszystkich otwartych plikach:

:argdo %s/Search/Replace/gce

Jednak kiedy to robię, użycie pamięci Vima skacze z kilkudziesięciu MB do ponad 2 GB, co nie działa dla mnie.

Mam też zainstalowaną wtyczkę EasyGrep , ale prawie nigdy nie działa - albo nie znajduje wszystkich wystąpień, albo po prostu zawiesza się, dopóki nie naciśnę CtrlC. Do tej pory moim preferowanym sposobem wykonania tego zadania jest zatwierdzenie grep dla wyszukiwanego hasła, używając jego okna quickfix otwórz dowolny plik, który zawiera termin i nie był wcześniej otwierany, i na koniec:bufdo %s/Search/Replace/gce .

Szukam albo dobrej, działającej wtyczki, którą można do tego użyć, albo alternatywnie polecenia / sekwencji poleceń, które byłyby łatwiejsze niż to, którego teraz używam.


1
@Cascabel Odkąd napisałeś ten komentarz, istnieje witryna vi.stackexchange.com.
Flimm

Odpowiedzi:


27

Greplace dobrze mi odpowiada.

Na githubie dostępna jest również wersja gotowa na patogen .


8
Zainstalowałem go, widzę inne polecenia, takie jak :Gsearchi :Gbuffersearchistnieją, ale kiedy piszę :Greplace, otrzymuję Not an editor command: Greplace.
Ramon Tayag

zgodnie z dokumentacją składnia jest następująca: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] więc możesz przeprowadzić wyszukiwanie rekurencyjne, używając na przykład::Gsearch -r pattern dir/
vitorbal

To, czego nienawidzę w Greplace, to to, że jeśli wzorzec znajduje się w nazwie pliku, Greplace zawodzi, ponieważ w końcu zastępujesz go również w nazwie pliku.
Daniel

2
Nic dziwnego tutaj po 6 latach, ale ta wtyczka jest poważnie przestarzała w porównaniu z niektórymi nowymi odpowiedziami ( github.com/yegappan/greplace ) z ostatniej aktualizacji, 3+ lata temu. Mogę sprawdzić wbudowane rozwiązanie Sid: stackoverflow.com/a/38004355/2224331 (To nie ja na innym koncie, tylko zbieg okoliczności: P)
SidOfc

99

Inną dużą opcją jest po prostu nieużywanie vim:

sed -i 's/pattern/replacement/' <files>

lub jeśli masz jakiś sposób na wygenerowanie listy plików, być może coś takiego:

find . -name *.cpp | xargs sed -i 's/pattern/replacement/'
grep -rl 'pattern1' | xargs sed -i 's/pattern2/replacement/'

i tak dalej!


Dzięki! Naprawdę muszę się nauczyć tej starej szkoły poleceń wiersza poleceń. Myślę, że to najlepsza odpowiedź. Czy te wyrażenia regularne są wyrażeniami regularnymi perl, wyrażeniami regularnymi Vima, czy też innymi typami wyrażeń regularnych?
Eric Johnson,

2
@EricJohnson: Są to ... sedwyrażenia regularne, które są zasadniczo podstawowymi wyrażeniami regularnymi POSIX.2, ale nie do końca (jest to na stronie podręcznika) - pozwalają \ndopasować znaki nowej linii i kilka innych podobnych rzeczy. Dlatego są prawie takie same jak grepwyrażenia regularne.
Cascabel,

Czy jest powód, dla którego masz -i w poleceniu sed? Strona podręcznika mówi, że służy do określania rozszerzenia, ale nie wydaje się, abyś je określał.
davekaro

Ok, faktycznie wygląda na to, że 's / pattern / zamiennik /' to rozszerzenie, myślę, że teraz rozumiem.
davekaro

11
W systemie Mac OS X jedynym poleceniem seda, które działało dla mnie, było:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss

74

EDYCJA: Użyj cfdopolecenia zamiast, cdoaby znacznie zmniejszyć liczbę poleceń, które zostaną uruchomione, aby to osiągnąć (ponieważ cdouruchamia polecenia na każdym elemencie, gdycfdo uruchamia polecenia na każdym pliku)

Dzięki niedawno dodanemu cdopoleceniu możesz teraz zrobić to za pomocą dwóch prostych poleceń przy użyciu dowolnego grepzainstalowanego narzędzia. Nie są wymagane żadne dodatkowe wtyczki !:

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

( cdowykonuje daną komendę do każdego terminu z listy szybkich poprawek, do której plikgrep wypełnia polecenie).

(Usuń znak cna końcu drugiego polecenia, jeśli chcesz zamienić każdy wyszukiwany termin bez potwierdzania za każdym razem)


12
Dodatkowo możesz dodać, :cdo updateaby zapisać zmiany we wszystkich plikach.
Zhe Li

1
Ale zatrzyma się, aby ostrzec, że nie jest zapisywany za każdym razem przed przełączeniem do następnego bufora, gdy bieżący bufor zostanie zmodyfikowany.
Wolny

1
@Zhe dzięki, dodałem to do odpowiedzi; @lfree Osobiście wolę potwierdzać każdą zmianę, ale możesz usunąć cna końcu drugiego polecenia (po prostu użyj g), aby wyszukać i zamienić bez pytania o potwierdzenie
Sid

1
: cdo% s / szukany termin / zamień termin / g zastępuje tylko pierwsze wystąpienie, ale nie działa przy drugim wystąpieniu w moim vimie
sunxd

3
za korzystanie updatemusiałem:cdo %s/<search term>/<replace term>/g | update
gabe

34

Zdecydowałem się użyć polecenia ACK i Perla, aby rozwiązać ten problem, aby skorzystać z potężniejszych pełnych wyrażeń regularnych Perla, zamiast podzbioru GNU.

ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'

Wyjaśnienie

ACK

ACK jest niesamowite narzędzie wiersza polecenia, które jest mieszanką grep, findi pełne wyrażenia regularne Perl (nie tylko podzbiór GNU). Jest napisany w czystym Perlu, jest szybki, ma podświetlanie składni, działa w systemie Windows i jest bardziej przyjazny dla programistów niż tradycyjne narzędzia wiersza poleceń. Zainstaluj go na Ubuntu z sudo apt-get install ack-grep.

xargs

Xargs to stare narzędzie wiersza poleceń systemu Unix. Odczytuje elementy ze standardowego wejścia i wykonuje określone polecenie, a po nim elementy odczytane ze standardowego wejścia. W zasadzie lista plików wygenerowanych przez potwierdzenie jest dołączana na końcu perl -pi -E 's/pattern/replacemnt/g'polecenia.

perl -pi

Perl to język programowania. Opcja -p powoduje, że Perl tworzy pętlę wokół twojego programu, która iteruje po argumentach nazw plików. Opcja -i powoduje, że Perl edytuje plik w miejscu. Możesz to zmodyfikować, aby tworzyć kopie zapasowe. Opcja -E powoduje, że Perl wykonuje jedną linię kodu określoną jako program. W naszym przypadku program jest po prostu podstawieniem wyrażenia regularnego w Perlu. Więcej informacji na temat opcji wiersza poleceń Perla perldoc perlrun. Więcej informacji na temat Perla można znaleźć pod adresem http://www.perl.org/ .


Podoba mi się ta odpowiedź, ponieważ działa nawet z zakończeniami linii cygwina. Zauważ, że dodaje \ r na końcu zmodyfikowanych linii, ale kiedy używasz seda, podmienia każdą nową linię , sprawiając, że różnice nie będą użyteczne. Perl jest trochę bardziej rozsądny i jest to naprawdę świetny jednolinijkowy element do wyszukiwania i zastępowania. Dzięki!
andrew

@andrew, nie jestem pewien, co się dzieje w twoim przypadku. Czy pomogłoby ci użycie \ R zamiast \ n w swoich wyrażeniach regularnych? Zobacz sekcję \ R w perldoc.perl.org/perlrebackslash.html#Misc . Spróbuj także perldoc.perl.org/perlre.html#Regular-Expressions . Perl jest bardzo wieloplatformowy i jestem pewien, że istnieje sposób, aby nie skończyć z zniekształconymi zakończeniami linii.
Eric Johnson

Moje wyrażenie regularne nie zawiera żadnych nowych linii, wersje tych programów w cygwin automatycznie dodają \ r na końcu każdej zmodyfikowanej linii. Szczególnie podobała mi się wersja Perla, ponieważ modyfikuje tylko wiersze, do których pasuje, podczas gdy sed zmodyfikuje wszystkie wiersze, nawet jeśli nie pasują do wyrażenia regularnego.
andrew

Co się stanie, jeśli ścieżka pliku zawiera spacje?
shengy

23

może zrób to:

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

Wyjaśnienie:

  • :noautocmd vim /Search/ **/*⇒ lookup ( vimto skrót od vimgrep) wzorzec we wszystkich plikach we wszystkich podkatalogach cwd bez wyzwalania autocmds ( :noautocmd), ze względu na szybkość.
  • :set hidden ⇒ zezwalaj na wyświetlanie zmodyfikowanych buforów w oknie (może być w twoim vimrc)
  • :cfirst ⇒ przejdź do pierwszego wyniku wyszukiwania
  • qa ⇒ rozpocząć zapis makra do rejestru a
  • :%s//Replace/gce⇒ zamień wszystkie wystąpienia ostatniego wzorca wyszukiwania (jeszcze /Search/w tym czasie) naReplace :
    • kilka razy w tej samej linii (g flaga)
    • z potwierdzeniem użytkownika (c flaga)
    • bez błędu, jeśli nie znaleziono wzoru ( eflaga)
  • :cnf ⇒ przejdź do następnego pliku na liście utworzonej przez vim polecenie
  • q ⇒ zatrzymaj nagrywanie makra
  • 1000@a ⇒ odtwórz makro zapisane w rejestrze 1000 razy
  • :wa ⇒ zapisz wszystkie zmodyfikowane bufory

* EDYTOWAĆ * Vim 8 sposób:

Począwszy od Vim 8 jest lepszy sposób na zrobienie tego, ponieważ :cfdowykonuje iteracje na wszystkich plikach na liście quickfix:

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

może zechcesz to wyjaśnić nieco dalej dla nas, pomniejszych śmiertelników. Nie jest dla mnie jasne, czy za każdym razem musisz wykonywać tę „sekwencję”. Robię to szybciej, używając mojego starego, zardzewiałego Delphi 5, więc na pewno czegoś mi brakuje.
Lieven Keersmaekers

1
@Lieven: Masz rację, jest to trochę niejasne bez komentarzy. Opracuję pewne wyjaśnienie.
Benoit,

+1 ale chociaż vimprzekonał mnie do wielu rzeczy, myślę, że pozostanę przy tym PowerGrep.
Lieven Keersmaekers

Dziękuję za odpowiedź i wyjaśnienie wszystkich poleceń, naprawdę to doceniam. Akceptuję drugą odpowiedź, ponieważ wolę do tego użyć wtyczki.
psyho

Oznaczyłem to, ponieważ powoduje więcej zamieszania, biorąc pod uwagę rozbieżność między poziomem osoby zadającej pytanie a poziomem wymaganym do zrozumienia tej odpowiedzi.
Lloyd Moore

22

Wypełnij za :argspomocą polecenia powłoki

Możliwe jest (w niektórych systemach operacyjnych 1 )) dostarczenie plików za :argspomocą polecenia powłoki.

Na przykład, jeśli masz zainstalowany program ACK 2 ,

:args `ack -l pattern`

poprosi ACK o zwrócenie listy plików zawierających „wzorzec” i umieszczenie ich na liście argumentów.

Lub zwykłym starym grepem, myślę, że będzie:

:args `grep -lr pattern .`  


Następnie możesz po prostu użyć :argdozgodnie z opisem w OP:

:argdo %s/pattern/replacement/gce


Wypełnianie :argsz listy quickfix

Sprawdź również odpowiedź nelstroma na powiązane pytanie opisujące proste polecenie zdefiniowane przez użytkownika, które wypełnia arglistę z bieżącej listy szybkich poprawek. Działa to świetnie z wieloma poleceniami i wtyczkami, których wyjście kończy się na liście quickfix ( :vimgrep, :Ack3 , :Ggrep4 ).

Sekwencję wyszukiwania całego projektu można następnie wykonać za pomocą:

:vimgrep /pattern/ **/*
:Qargs 
:argdo %s/findme/replacement/gc

gdzie :Qargsjest wywołaniem polecenia użytkownika, które wypełnia arglistę z listy quickfix.

Linki znajdziesz również w następującej dyskusji do prostych wtyczek, które sprowadzają ten przepływ pracy do 2 lub 3 poleceń.

Spinki do mankietów

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ACK - Betterthangrep.com/
  3. Ack.vim - github.com/mileszs/ack.vim
  4. uciekinier - github.com/tpope/vim-fugitive

1
argdo zatrzymuje się po pierwszym pliku z problemem z zapisem
frankster


5

Jeśli nie masz nic przeciwko wprowadzeniu zewnętrznej zależności, przygotowałem wtyczkę ctrlsf.vim (zależy od ACK lub AG ), aby wykonać zadanie.

Może formatować i wyświetlać wyniki wyszukiwania z ACK / AG oraz synchronizować zmiany w buforze wyników z rzeczywistymi plikami na dysku.

Może poniższe demo wyjaśnia więcej

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

Krok 1 wypełnia listę quickfix żądanymi elementami. W tym przypadku jest on propagowany z wyszukiwanymi hasłami, które chcesz zmienić grep.

cfdouruchamia następujące polecenie dla każdego pliku z listy quickfix. Wpisz :help cfdoszczegóły.

s/<search term>/<replace term>/gzastępuje każdy termin. /goznacza zastąpienie każdego wystąpienia w pliku.

| update zapisuje plik po każdej zamianie.

Złożyłem to razem w oparciu o tę odpowiedź i jej komentarze, ale uznałem , że zasługuje na własną odpowiedź, ponieważ wszystko jest w jednym miejscu.


U mnie na NeoVimie potrzebna była niewielka modyfikacja w linii 2 powyżej: 2. :cfdo %s/<search term>/<replace term>/g | updatebez tego %, tylko pierwsze wystąpienie wzorca jest zastępowane.
Kareem Ergawy

Dzięki Kareem. Zaktualizowałem odpowiedź, ponieważ %sdziała również na zwykłym vimie.
Kyle Heironimus

0

Zasadniczo chciałem zastąpić w jednym poleceniu, a co ważniejsze w samym vimie

Na podstawie odpowiedzi @Jefromi utworzyłem skrót klawiaturowy, który ustawiłem w moim pliku .vimrc w ten sposób

nmap <leader>r :!grep -r -l  * \| xargs sed -i -e 's///g'

teraz z vima, za jednym pociągnięciem lidera + r otrzymuję polecenie załadowane w vimie, które edytuję jak poniżej,

:!grep -r -l <find> <file pattern> | xargs sed -i -e 's/<find>/<replace>/g'

Dlatego zastępuję za pomocą jednego polecenia i, co ważniejsze, w samym vimie


i faktycznie używam ag zamiast grep
deepak
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.