Używanie Emacsa do rekurencyjnego znajdowania i zastępowania w plikach tekstowych, które nie są jeszcze otwarte


212

W odpowiedzi na to pytanie próbuje dowiedzieć się, jak zrobić coś takiego, co powinno być łatwe, co szczególnie powstrzymuje mnie przed przyzwyczajeniem się do korzystania z Emacsa i zamiast tego uruchomieniem edytora, który już znam. Korzystam z tego przykładu dość często podczas edycji wielu plików.

W Ultraedit zrobiłbym Alt + s, a następnie p, aby wyświetlić okno dialogowe z opcjami: Znajdź (obejmuje użycie wyrażeń regularnych w wielu wierszach), Zamień na, W plikach / typach, Katalogu, Dopasuj wielkość liter, Dopasuj tylko całe słowo, Lista Zmienione pliki i podkatalogi wyszukiwania. Zwykle najpierw użyję myszy, aby przeciągnąć i zaznaczyć tekst, który chcę zastąpić.

Używając tylko samego Emacsa (w systemie Windows XP), bez wywoływania jakiegokolwiek zewnętrznego narzędzia, jak zamienić wszystkie foo \ nbar na bar \ nbaz w *.ci *.hpliki w niektórych folderach i wszystkich folderach pod nim. Może Emacs nie jest najlepszym narzędziem do zrobienia tego, ale jak można to łatwo zrobić za pomocą minimalnego polecenia?

Odpowiedzi:


370
  1. M-x find-name-dired: pojawi się monit o podanie katalogu głównego i wzorca nazwy pliku.
  2. Naciśnij, taby „przełączyć znacznik” dla wszystkich znalezionych plików.
  3. Naciśnij przycisk Q„Zamień zapytanie w plikach ...”: pojawi się monit o wyrażenie regularne zapytania / podstawienia.
  4. Postępuj jak w query-replace-regexp: SPACEzamień i przejdź do następnego meczu, naby pominąć mecz itp.
  5. Naciśnij, C-x saby zapisać bufory. (Można następnie nacisnąć przycisk y, nlub !, aby zapisać wszystkie naraz)

10
Nie, to rekurencyjne. Wersja nierekurencyjna byłaby% mw trybie dired.
Chris Conway,

5
Może dlatego, że wywołujesz C: \ WINDOWS \ system32 \ find.exe, a nie GNU find.
Jazz,

6
Steen, Chris: Prawdopodobnie nie dokładnie to, czego chcesz, ale możesz użyć „Cx s” (save-some-buffers), wyświetli monit o każdy zmodyfikowany plik, więc musisz nacisnąć tylko „y” wiele razy.
danielpoe

48
C-x s !aby zapisać wszystkie pliki jednocześnie.
cyr

10
M-,aby kontynuować wyszukiwanie i zamienić (na przykład po monicie „przywróć plik”, jeśli plik został zmieniony na dysku).
tfischbach,

37
  • M-x find-name-dired RET
    • pojawienie się wszystkich plików na liście może zająć trochę czasu, przewiń do dołu ( M->), aż find finishedpojawi się „ ”, aby upewnić się, że wszystkie zostały załadowane
  • Naciśnij, taby „przełączyć znacznik” dla wszystkich znalezionych plików
  • Naciśnij przycisk Q„Zamień zapytanie w plikach ...”: pojawi się monit o wyrażenie regularne zapytania / podstawienia.
  • Postępuj jak w przypadku zapytania-zamień-wyrażenie regularne: SPACJA lub w ycelu zastąpienia i przejścia do następnego dopasowania, n, aby pominąć dopasowanie itp.
    • Rodzaj ! aby zastąpić wszystkie wystąpienia w bieżącym pliku bez pytania, Npomiń wszelkie możliwe zamiany na resztę bieżącego pliku. ( Ntylko emacs 23+)
    • Aby zastąpić wszystkie pliki bez dalszego pytania, wpisz Y.
  • Zadzwoń do „ibuffer” ( C-x C-bjeśli jest powiązany z ibuffer lub M-x ibuffer RET), aby wyświetlić listę wszystkich otwartych plików.
  • Wpisz, * uaby zaznaczyć wszystkie niezapisane pliki, wpisz, Saby zapisać wszystkie zaznaczone pliki
  • * * RETaby odznaczyć wszystkie znaki, lub wpisz, Daby zamknąć wszystkie zaznaczone pliki

Ta odpowiedź jest połączona z tą odpowiedzią , z tej strony i z moich własnych notatek. Korzystanie z Emacsa 23+.


3
ibuffernie jest C-x C-bdomyślnie związany.
phils,

2
Dobrze osobiście polecam że ludzie nie wiążą C-x C-bsię ibuffer, bo to o wiele lepsze niż wiązania domyślnie. Po prostu dodaj (global-set-key (kbd "C-x C-b") 'ibuffer)do pliku .emacs. W przeciwnym razie skorzystaj M-x ibuffer RETz powyższych instrukcji.
phils

dobrze.
Wydaje

1
Plik bla-bla-blajest odwiedzany tylko do odczytu i zatrzymuje się na wyszukiwaniu. Jak tego uniknąć / zignorować i ciągle wymieniać? Dzięki.
Felipe

3
Dlaczego, ibufferkiedy możesz C-x s( save-some-buffers) i pisać !? Nie chcę być wściekły, tylko ciekawy. PS Plus do opisania !, Ni Y.
d12 zamrożone

17

Odpowiedzi są świetne, ale pomyślałem, że dodam nieco inne podejście.

Jest to metoda bardziej interaktywny i wymaga wgrep, rgrepi iedit. Zarówno iediti wgrepmusi być zainstalowany przez MELPA lub Marmalade (za pomocą M-x package-list-packages)

Najpierw biegnij, M-x rgrepaby znaleźć szukany ciąg.

Będziesz mógł określić typy plików / wzorzec i folder do ponownego wykorzystania.

Następnie musisz uruchomić, wgrepzacznij od C-s C-p.

Wgrep pozwoli Ci edytować rgrepwyników, więc ustawić region na ciąg dopasować i rozpocząć iedit-modez C-;(w zależności od terminala może być konieczne ponowne wiążą ten temat)

Wszystkie wystąpienia będą edytowalne jednocześnie. C-x C-spopełnić wgrep. Następnie, C-x s !aby zapisać zmienione pliki.

Główną zaletą tej metody jest to, że można jej użyć iedit-modedo przełączania niektórych dopasowań M-;. Możesz także użyć wyników w, rgrepaby przejść do plików, na przykład, jeśli masz nieoczekiwane dopasowanie.

Uważam, że jest to bardzo przydatne do edycji źródeł i zmiany nazw symboli (zmiennych, nazw funkcji itp.) W całym projekcie.

Jeśli jeszcze nie znasz / nie używasz trybu iedit, jest to bardzo przydatne narzędzie, zdecydowanie zalecamy, abyś rzucił okiem.


Ta technika jest dość potężna nawet bez użycia iedit. Okazuje się, jak umieć grepować (łatwo z lgrep / rgrep) w wiedzę, jak zbiorczo wyszukiwać-zamieniać!
NeilenMarais,

Łączenie rgrep / wgrep / macros jest niesamowite.
ocodo

2
Keybinding na początek wgrepjest przy C-c C-pokazji.
techniao


13

Zasadniczo używam innych narzędzi do wykonania tego zadania i wydaje się, że wiele podejść wymienionych w EmacsWiki w powłoce wejściowej Znajdź i zamień pliki jest obecnie dostępna, ale pakiet Findr wygląda bardzo obiecująco.

Kradzież części pliku źródłowego :

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.

jak próbuję znaleźć ciąg „Collectible” we wszystkich plikach z rozszerzeniem java i hxx przy użyciu findr.el?
swdev

@swdev: Mx findr-query-replace RET Collectible RET <zamiana> RET. * \. java RET <katalog, w którym należy rozpocząć wyszukiwanie> RET
ShreevatsaR

Podoba mi się to podejście (pozwala uniknąć wszystkich przełączeń związanych z podejściem bezpośrednim). Korzystam z niektórych funkcji wygody, aby połączyć to z pociskiem (project-root-Discover) i zastąpić rzecz w punkcie: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ

6

Źródło informacji: 1

Dla użytkowników emacs pro:

  1. Wywołaj dired, aby wyświetlić listę plików w katalogu lub zadzwoń znajdź dired, jeśli potrzebujesz wszystkich podkatalogów.
  2. Zaznacz żądane pliki. Możesz zaznaczyć regex, wpisując 【% m】.
  3. Wpisz Q, aby wywołać dired-do-query-replace-regexp.
  4. Wpisz szukane wyrażenie regularne i zamień ciąg. ☛ ☛ wspólny wzór wyrażenia regularnego elisp〕
  5. Dla każdego wystąpienia wpisz y, aby zastąpić, n, aby pominąć. Wpisz 【Ctrl + g】, aby przerwać całą operację.
  6. Rodzaj ! aby zastąpić wszystkie wystąpienia w bieżącym pliku bez pytania, N, aby pominąć wszelkie możliwe zamiany pozostałej części bieżącego pliku. (N to tylko emacs 23)
  7. Aby wykonać zamianę wszystkich plików bez dalszego pytania, wpisz Y. (tylko Emacs 23)
  8. Zadzwoń do ibuffera, aby wyświetlić listę wszystkich otwartych plików. Wpisz 【* u】, aby zaznaczyć wszystkie niezapisane pliki, wpisz S, aby zapisać wszystkie zaznaczone pliki, wpisz D, aby je wszystkie zamknąć.

Przewodnik krok po kroku dla początkujących graczy Emacsa

Wybierz pliki docelowe

Uruchom emacsa, wpisując „emacs” w wierszu poleceń interfejsu wiersza poleceń. (Lub kliknij dwukrotnie ikonę Emacsa, jeśli jesteś w środowisku graficznego interfejsu użytkownika)

Wybieranie plików w katalogu

Najpierw musisz wybrać pliki, które chcesz zastąpić. Skorzystaj z menu graficznego 〖Plik ▸ Otwórz katalog〗. Emacs poprosi cię o ścieżkę do katalogu. Wpisz ścieżkę katalogu, a następnie naciśnij klawisz Enter.

Teraz zostanie wyświetlona lista plików, a teraz musisz zaznaczyć pliki, na których ma działać regex find / replace. Zaznacz plik, przesuwając kursor do żądanego pliku, a następnie naciśnij m. Usuń zaznaczenie, naciskając u. (Aby wyświetlić listę podkatalogów, przenieś kursor do katalogu i naciśnij i. Zawartość podkatalogu zostanie wyświetlona na dole.) Aby oznaczyć wszystkie pliki za pomocą wyrażenia regularnego, wpisz 【% m】, a następnie wpisz wzorzec wyrażenia regularnego. Na przykład, jeśli chcesz oznaczyć wszystkie pliki HTML, wpisz 【% m】, a następnie .html $. (Listę komend oznaczania można znaleźć w menu graficznym „Mark” (to menu pojawia się, gdy jesteś w trybie dired).)

Wybieranie plików w katalogu i wszystkich jego podkatalogach

Jeśli chcesz znaleźć / zamienić na pliki w katalogu, w tym setki podkatalogów, oto metoda wyboru wszystkich tych plików.

Zadzwoń znajdź-dired. (wywołujesz polecenie, naciskając 【Alt + x】) Następnie wpisz nazwę katalogu ⁖ / Users / mary / myfiles

Uwaga: jeśli używasz emacsa na nie graficznym terminalu tekstowym unix, a jeśli 【Alt + x】 nie działa, równoważne naciśnięcie klawisza to 【Esc x】.

Emacs poprosi cię o komunikat „Uruchom znajdź (z argumentami):”. Aby zastąpić wszystkie pliki HTML, wpisz -name „* html”. Jeśli nie obchodzi Cię, jakiego rodzaju plik, ale po prostu wszystkie pliki w tym katalogu, podaj „-type f”.

Teraz zaznacz pliki zgodnie z powyższym opisem.

Interaktywne wyszukiwanie / zamiana

Teraz jesteś gotowy, aby zastąpić wyszukiwanie interaktywne. Dla uproszczenia załóżmy, że chcesz po prostu zastąpić słowo „szybki” słowem „super”. Teraz wywołaj dired-do-query-replace-regexp. Poprosi Cię o ciąg wyrażenia regularnego i ciąg zastępujący. Wpisz „szybkie”, wpisz, a następnie „super”.

Teraz emacs użyje twojego wzorca i sprawdzi pliki, zatrzyma się i pokaże za każdym razem, gdy wystąpi dopasowanie. Kiedy tak się stanie, emacs wyświetli monit, a Ty możesz dokonać zmiany lub pominąć zmianę. Aby wprowadzić zmianę, wpisz y. Aby pominąć, wpisz n. Jeśli po prostu chcesz, aby emacs kontynuował i wprowadził wszystkie takie zmiany w bieżącym pliku, wpisz!

Jeśli chcesz anulować całą operację bez zapisywania wprowadzonych zmian, wpisz 【Ctrl + g】, a następnie zakończ emacsa za pomocą menu 〖Plik ▸ Wyjdź z Emacsa〗.

Zapisywanie zmienionych plików

Teraz, po przejściu powyższej próby, musisz zrobić jeszcze jeden krok, a mianowicie zapisać zmienione pliki.

Jeśli używasz emacsa w wersji 22 lub nowszej, wywołaj ibuffer, aby przejść do trybu listy buforów, a następnie wpisz type * u】, aby zaznaczyć wszystkie niezapisane pliki, a następnie wpisz S, aby je wszystkie zapisać. (to zmiany)

Jeśli używasz emacsa w wersji 21, możesz to zrobić: wywołać bufory listy, a następnie przesunąć kursor do pliku, który chcesz zapisać i wpisać s. Zaznacza plik do późniejszego zapisania. Wpisz u, aby odznaczyć. Po zakończeniu wpisz x, aby uruchomić zapisywanie wszystkich plików oznaczonych do zapisania. (w emacs otwarty plik nazywa się „buforem”. Pomiń inne rzeczy.)

Alternatywnie do powyższych opcji możesz także wywołać save-some-buffers 【Ctrl + xs】. Następnie emacs wyświetli każdy niezapisany plik i zapyta, czy chcesz go zapisać.

Uwaga: wyrażenie regularne emacsa nie jest takie samo, jak Perl lub Python, ale podobne. Aby uzyskać podsumowanie i typowe wzorce, zobacz: Emacs Regex.


2
Ta odpowiedź powinna uczynić ją bardziej przyjazną dla nowicjuszy niż najczęściej głosowaną odpowiedź, którą mam nadzieję. Dzięki przy okazji, za motywowanie mnie do umieszczenia całości zamiast linku.
Prashant Sharma

Odpowiedź zaczyna się od źródła: jako pierwszego wiersza. Uzasadnieniem dla wklejania kopii jest: czasami zewnętrzny link do blogów staje się nieaktualny, a wtedy odpowiedź staje się bezużyteczna. I tak jak sugeruje meta wiki. Postanowiłem skopiować całość.
Prashant Sharma

5

Używanie dired do rekurencji w głębokim drzewie katalogów będzie nieco powolne w przypadku tego zadania. Możesz rozważyć użycie tagu-query-replace . Oznacza to, że można utworzyć tabelę znaczników, ale i tak jest to często przydatne i jest szybkie.


Właśnie wypróbowałem katalog zawierający ponad 14 000 plików. Kilka sekund zajęło wyskakujące okienko w Emacsie. Zdecydowanie nie jest już wolny (więcej).
Berend de Boer,

3

W przypadku otwartych buforów robię to:

(defun px-query-replace-in-open-buffers (arg1 arg2)
  "query-replace in all open files"
  (interactive "sRegexp:\nsReplace with:")
  (mapcar
   (lambda (x)
     (find-file x)
     (save-excursion
       (goto-char (point-min))
       (query-replace-regexp arg1 arg2)))
   (delq
    nil
    (mapcar
     (lambda (x)
       (buffer-file-name x))
     (buffer-list)))))

3

M-X Dired, oraz t, aby zaznaczyć wszystkie pliki i Qwysłać zapytanie o zamianę tekstu we wszystkich. Możesz rozwinąć podkatalog za pomocą ipolecenia przed zastąpieniem zapytania. Kluczowe informacje, które dodaję, to to, że jeśli podasz prefiks (control-u) komendzie i, poprosi cię o arg, a argument -R rekurencyjnie rozszerzy wszystko subdirs do diredbufora. Teraz możesz wyszukiwać w każdym pliku w całym katalogu.


2

Inną opcją jest użycie wyszukiwania Sople . Jest to inny rodzaj wyszukiwania przyrostowego, który wykorzystuje uzupełnienie danych wejściowych minibufora względem wyników wyszukiwania. Po zmodyfikowaniu bieżącego wejścia zestaw pasujących trafień jest aktualizowany w buforze*Completions* .

Możesz przeszukiwać dowolną liczbę plików, buforów lub lokalizacji zakładek, które możesz wybrać za pomocą dopasowania wzorca minibufora (np. Regexp).

Podczas odwiedzania wyszukiwania możesz zastąpić na żądanie całe trafienie lub tylko jego część pasującą do Twojego aktualnego wejścia do minibufora. Zastąpienie na żądanie oznacza, że ​​nie jesteś pytany o każde trafienie z kolei; masz dostęp do trafień, które chcesz bezpośrednio, w dowolnej kolejności. Takie podejście może być bardziej skuteczne niż zamiana zapytań, jeśli masz ograniczoną liczbę zamienników do wykonania: pomijasz wyczerpujące y/npytania.

Wyszukiwanie obejmuje zdefiniowane konteksty wyszukiwania - nie jesteś ograniczony do przeszukiwania całego tekstu w plikach docelowych (np. Możesz pominąć komentarze lub poszczególne rodzaje sekcji programu). Prostym przykładem kontekstu wyszukiwania jest linia, jak wgrep , ale kontekstem może być dowolny blok tekstu dopasowany do wzorca. Zazwyczaj definiujesz konteksty wyszukiwania za pomocą wyrażenia regularnego, ale zamiast tego możesz użyć funkcji. Oprócz definiowania własnego, istnieją predefiniowane polecenia wyszukiwania w Soplach dla różnych rodzajów kontekstów: bloki właściwości tekstu lub właściwości nakładki, rzeczy w punkcie itp.

Możesz także sortować wyniki wyszukiwania w różnych porządkach sortowania dla łatwiejszego dostępu / nawigacji.


2

find-name-dired jest OK, ale:

  • Wszystkie otrzymane pliki pasują do tego samego, pojedynczego wyrażenia regularnego.
  • find-diredjest pod tym względem bardziej elastyczny, ale również służy do stosowania ogólnych zasad (nawet jeśli mogą być dowolnie złożone). I oczywiście findma swój własny, złożony język.
  • jeśli chcesz działać tylko na niektórych plikach, których nazwy zostały zebrane w find(-name)-diredbuforze, musisz je zaznaczyć lub usunąć / pominąć wiersze tych, na których nie chcesz działać.

Alternatywą jest użycie poleceń Dired + , które działają na (a) zaznaczone pliki i (b) wszystkie zaznaczone pliki (lub wszystkie pliki, jeśli żaden nie jest zaznaczony) w zaznaczonych podkatalogach ... znalezione rekurencyjnie . Daje to zarówno ogólność, jak i łatwą kontrolę nad wyborem pliku. Te polecenia „tu i poniżej” są na kluczu prefiksu M-+w trybie Dired.

Na przykład M-+ Qjest taki sam jak Q--- zamień zapytanie, ale wszystkie pliki docelowe są wszystkie oznaczone w bieżącym katalogu i we wszystkich zaznaczonych podkatalogach, w dół, w dół, w dół ...

Tak, alternatywą dla używania takich tu i poniżej poleceń jest rekurencyjne wstawianie wszystkich podkatalogów i ich podkatalogów, a następnie użycie polecenia najwyższego poziomu, takiego jak Q. Ale często wygodne może być nie zawracanie sobie głowy wstawionymi podkatalogami.

Aby to zrobić, i tak potrzebujesz szybkiego sposobu na rekursywne wstawienie wszystkich takich podkatalogów . Również tutaj Dired + może pomóc. M-+ M-iwstawia rekurencyjnie wszystkie zaznaczone podkatalogi i własne oznaczone podkatalogi. Oznacza to, że jest jak M-i(który wstawia zaznaczone podkatalogi w Dired + ), ale działa rekurencyjnie na podkatalogach.

(Wszystkie takie polecenia „tu i poniżej” Dired + znajdują się w menu Wiele > Zaznaczone tutaj i poniżej .)

Możesz także wykonywać operacje Dired na zestawie plików Emacsa , który jest zapisanym zestawem nazw plików znajdujących się w dowolnym miejscu. A jeśli używasz Sople wtedy można otworzyć Dired bufor dla zaledwie plików w zestawu plików lub innych typów plików zapisanych list.

Możesz także dodać do zakładek dowolny bufor Dired, w tym bufor utworzony za pomocą find(-name)-dired. To daje szybki sposób na powrót do takiego zestawu (np. Zestawu projektu) później. A jeśli użyjesz Zakładki +, dodaj do zakładek Dired bufor zapisuje (a) jego lsprzełączniki, (b) które pliki są zaznaczone, (c) które podkatalogi są wstawione i (d) które (podk) katalogi są ukryte. Wszystko to jest przywracane, gdy „przeskakujesz” do zakładki. Zakładka + pozwala także dodać do zakładek całe drzewo Dired buforów --- przejście do zakładki przywraca wszystkie bufory w drzewie.


Ktokolwiek: Chcesz wyjaśnić, dlaczego głosowałeś za tą odpowiedzią?
Drew

1

Chciałbym zasugerować jeszcze jedno świetne narzędzie, o którym jeszcze nie wspomniano, mianowicie Helm .

Jest doskonałym zamiennikiem wielu standardowych operacji Emacsa obejmujących uzupełnianie, wyszukiwanie itp. W szczególności helm-find-filesumożliwia wykonywanie zamiany zapytań (w tym wyrażeń regularnych) w wielu wybranych plikach.

Wystarczy otworzyć helm-find-files, zaznaczyć odpowiednie pliki za pomocą, M-SPCa następnie użyć F6lub, F7aby uruchomić zamianę zapytania lub zastąpić zapytanie regexp w wybranych plikach.


Czy helm-find-fileszamiast regexp można oznaczyć M-SPC?
Nordlöw,

-2

To nie jest Emacs, ale xxdiff jest wyposażony w narzędzie o nazwie xx-rename, które zrobi to dla wielu ciągów naraz (np. From To od do FROM TO), z interaktywnym monitowaniem, zapisz kopie zapasowe wszystkich zmodyfikowanych plików i wygeneruj krótki dziennik zmian dokonanych w kontekście. Tego właśnie używam, gdy dokonuję dużych / globalnych zmian nazw.

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.