Utilizzo di Emacs per trovare e sostituire ricorsivamente in file di testo non già aperti


212

Come seguito a questa domanda , sta cercando di scoprire come fare qualcosa del genere che dovrebbe essere facile, che mi impedisce soprattutto di abituarmi maggiormente all'utilizzo di Emacs e invece di avviare l'editor con cui ho già familiarità. Uso l'esempio qui abbastanza spesso nella modifica di più file.

In Ultraedit farei Alt + s quindi p per visualizzare una finestra di dialogo con le opzioni: Trova (include l'uso di espressioni regolari su più righe), Sostituisci con, In file / tipi, Directory, Trova maiuscole, Trova solo parole intere, Elenco File modificati e ricerca nelle sottodirectory. Di solito per prima cosa userò il mouse per fare clic e trascinare selezionare il testo che voglio sostituire.

Utilizzando solo Emacs stesso (su Windows XP), senza chiamare alcuna utility esterna, come sostituire tutto foo \ nbar con bar \ nbaz in *.ce *.hfile in alcune cartelle e tutte le cartelle sottostanti. Forse Emacs non è lo strumento migliore per farlo, ma come può essere fatto facilmente con un comando minimo?

Risposte:


370
  1. M-x find-name-dired: ti verrà richiesta una directory principale e un modello di nome file.
  2. Premi t"toggle mark" per tutti i file trovati.
  3. Premere Qper "Query-Replace in Files ...": verrà richiesto regexps query / sostituzione.
  4. Procedere come per query-replace-regexp: SPACEper sostituire e passare alla partita successiva, nper saltare una partita, ecc.
  5. Premere C-x sper salvare i buffer. (È possibile quindi premere y, no !per salvare tutto in una volta)

10
No, è ricorsivo. La versione non ricorsiva sarebbe% m in modalità Dired.
Chris Conway,

5
Forse perché stai chiamando C: \ WINDOWS \ system32 \ find.exe, non GNU find.
Jazz,

6
Steen, Chris: Probabilmente non esattamente quello che vuoi, ma puoi usare 'Cx s' (save-some-buffers), ti chiederà ogni file modificato, quindi devi solo premere 'y' più volte.
danielpoe,

48
C-x s !per salvare tutti i file contemporaneamente.
cYrus,

10
M-,per continuare la ricerca e la sostituzione (ad esempio dopo il prompt "ripristina file" se il file è cambiato sul disco).
tfischbach,

37
  • M-x find-name-dired RET
    • potrebbe essere necessario del tempo prima che tutti i file vengano visualizzati nell'elenco, scorrere fino in fondo ( M->) fino a quando " find finished" appare per assicurarsi che siano stati caricati tutti
  • Premi t"toggle mark" per tutti i file trovati
  • Premere Qper "Query-Replace in Files ...": verrà richiesto regexps query / sostituzione.
  • Procedere come con query-replace-regexp: SPACE o yper sostituire e passare alla partita successiva, n per saltare una partita, ecc.
    • Genere ! per sostituire tutte le occorrenze nel file corrente senza chiedere, Nper saltare tutte le possibili sostituzioni per il resto del file corrente. ( Nè solo emacs 23+)
    • Per eseguire la sostituzione su tutti i file senza chiedere altro, digitare Y.
  • Chiamare "ibuffer" ( C-x C-bse associato a ibuffer o M-x ibuffer RET) per elencare tutti i file aperti.
  • Digitare * uper contrassegnare tutti i file non salvati, digitare Sper salvare tutti i file contrassegnati
  • * * RETper deselezionare tutti i segni o digitare Dper chiudere tutti i file contrassegnati

Questa risposta è combinata da questa risposta , da questo sito e dalle mie note. Utilizzo di Emacs 23+.


3
ibuffernon è vincolato per C-x C-bimpostazione predefinita.
phils,

2
Beh personalmente raccomandano che le persone non si legano C-x C-ba ibuffer, perché è così molto meglio della rilegatura predefinita. Aggiungi (global-set-key (kbd "C-x C-b") 'ibuffer)al tuo file .emacs. In caso contrario, utilizzare M-x ibuffer RETnelle istruzioni sopra.
phils,

ok. Penso di avere il kit di avvio di emacs, motivo per cui il mio è legato
Frank Henard,

1
Il file bla-bla-blaviene visitato in sola lettura e si interrompe nella ricerca. Come evitarlo / ignorarlo e continuare a sostituirlo? Grazie.
Felipe,

3
Perché ibufferquando puoi C-x s( save-some-buffers) e digitare !? Non voglio essere furioso, solo curioso. PS più per descrivere !, Ne Y.
d12 congelato il

17

Le risposte fornite sono ottime, tuttavia ho pensato di aggiungere un approccio leggermente diverso.

Si tratta di un metodo più interattivo, e richiede wgrep, rgrepe iedit. Entrambi iedite wgrepdevono essere installati tramite MELPA o Marmalade (usandoM-x package-list-packages )

Prima corsa M-x rgrep per trovare la stringa che stai cercando.

Sarai in grado di specificare i tipi di file / modello e la cartella da ricorrere.

Successivamente dovrai eseguirlo wgrepavviarlo conC-s C-p .

Wgrep ti permetterà di modificare i rgreprisultati, quindi imposta un'area sulla stringa in modo che corrisponda e inizi iedit-modeconC-; (a seconda del terminale potrebbe essere necessario ri-bind questo)

Tutte le occorrenze saranno modificabili contemporaneamente. C-x C-simpegnarsi wgrep. PoiC-x s ! per salvare i file modificati.

Il vantaggio principale di questo metodo è che puoi utilizzare iedit-modeper disattivare determinate partite M-;. Puoi anche usare i risultati inrgrep per saltare nei file, ad esempio se hai una corrispondenza imprevista.

Lo trovo molto utile per apportare modifiche alla sorgente e rinominare simboli (variabili, nomi di funzioni ecc.) In un progetto.

Se non conosci / usi già la modalità iedit è uno strumento molto utile, ti consiglio vivamente di dare un'occhiata.


Questa tecnica è piuttosto potente anche senza usare iedit. Trasforma il sapere come grep (facile con lgrep / rgrep) nel sapere come sostituire la ricerca in blocco!
NeilenMarais,

Combinare rgrep / wgrep / macros è fantastico.
ocodo,

2
La legatura dei tasti per iniziare wgrepè C-c C-pbtw.
techniao

15

Proiettile è davvero bello: Cc pr esegue il comando proiettile-sostituisci


13

In genere utilizzo altri strumenti per eseguire questa attività e sembra che molti degli approcci menzionati nella voce Trova e sostituisci i file di EmacsWiki EmacsWiki , ma il pacchetto Findr sembra molto promettente.

Rubare parte del file sorgente :

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

come posso provare a trovare la stringa "Collezionabile" in tutti i file con estensione java e hxx usando findr.el?
swdev,

@swdev: Mx findr-query-replace RET RET da collezione <la sostituzione> RET. * \. java RET <la directory per iniziare la ricerca in> RET
ShreevatsaR

Mi piace questo approccio (evita tutti i toggling coinvolti nell'approccio diretto). Uso alcune funzioni di convenienza per combinare questo con il proiettile (project-root-discover), e sostituisco la cosa al punto: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ

6

Fonte di informazione: 1

Per gli utenti di emacs pro:

  1. Chiama dired per elencare i file in dir o chiama find-dired se hai bisogno di tutte le sottodirectory.
  2. Contrassegna i file desiderati. Puoi contrassegnare con regex digitando 【% m】.
  3. Digitare Q per chiamare dired-do-query-replace-regexp.
  4. Digita find regex e sostituisci la stringa. 〔☛ pattern regex elisp comune〕
  5. Per ogni occorrenza, digitare y per sostituire, n per saltare. Digita 【Ctrl + g】 per interrompere l'intera operazione.
  6. Genere ! per sostituire tutte le occorrenze nel file corrente senza chiedere, N per saltare tutte le possibili sostituzioni per il resto del file corrente. (N è solo emacs 23)
  7. Per eseguire la sostituzione su tutti i file senza ulteriore richiesta, digitare Y. (solo Emacs 23)
  8. Chiama ibuffer per elencare tutti i file aperti. Digitare 【* u】 per contrassegnare tutti i file non salvati, digitare S per salvare tutti i file contrassegnati, digitare D per chiuderli tutti.

Guida dettagliata per i principianti di Emacs

Seleziona file di destinazione

Avviare emacs digitando "emacs" nel prompt dell'interfaccia della riga di comando. (Oppure, fai doppio clic sull'icona Emacs se ti trovi in ​​un ambiente di interfaccia utente grafica)

Selezione dei file in una directory

Per prima cosa devi selezionare i file che vuoi sostituire. Utilizzare il menu grafico 〖File ▸ Apri directory〗. Emacs ti chiederà un percorso di directory. Digita il percorso della directory, quindi premi Invio.

Ora, ti verrà mostrato l'elenco dei file e ora devi contrassegnare i file su cui vuoi che Regex trovi / sostituisca per funzionare. Si contrassegna un file spostando il cursore sul file desiderato, quindi premere m. Deselezionalo premendo u. (Per elencare le sottodirectory, spostare il cursore sulla directory e premere i. Il contenuto della sottodirectory verrà elencato in fondo.) Per contrassegnare tutti i file con una regex, digitare 【% m】, quindi digitare il modello regex. Ad esempio, se si desidera contrassegnare tutti i file HTML, digitare 【% m】 quindi .html $. (Puoi trovare un elenco dei comandi mark nel menu grafico "Mark" (questo menu appare quando sei in modalità diretta).)

Selezione dei file in una directory e in tutte le sue sottodirectory

Se vuoi trovare / sostituire i file all'interno di una directory, incluse centinaia di sottodirectory, ecco un metodo per selezionare tutti questi file.

Chiama find-dired. (si chiama un comando premendo 【Alt + x】) Quindi, digitare un nome di directory, ⁖ / Users / mary / myfiles

Nota: se si utilizza emacs su un terminale di testo non grafico unix e se 【Alt + x】 non funziona, il tratto di tasto equivalente è 【Esc x】.

Emacs ti chiederà con il prompt "Esegui trova (con args):". Se è necessario eseguire la sostituzione su tutti i file HTML, digitare -name "* html". Se non ti interessa che tipo di file, ma semplicemente tutti i file in quella directory, allora dai "-type f".

Ora, segna i file come descritto sopra.

Trova / Sostituisci interattivo

Ora sei pronto per sostituire la ricerca interattiva. Per semplicità, diciamo che vuoi solo sostituire la parola "veloce" con "super". Ora chiama dired-do-query-replace-regexp. Ti verrà richiesta la stringa regex e la stringa di sostituzione. Digitare "veloce", inserire, quindi "super".

Ora, emacs utilizzerà il tuo modello e controllerà i file, si fermerà e ti mostrerà ogni volta che si verifica una corrispondenza. Quando ciò accade, ti verrà richiesto emacs e potrai scegliere se apportare la modifica o saltare la modifica. Per apportare la modifica, digitare y. Per saltare, digitare n. Se vuoi semplicemente che emacs continui e apporti tutte queste modifiche al file corrente, digita!.

Se vuoi annullare l'intera operazione senza salvare le modifiche apportate, digita 【Ctrl + g】, quindi esci da emacs usando il menu 〖File ▸ Esci da Emacs〗.

Salvataggio dei file modificati

Ora, dopo aver superato la prova precedente, c'è ancora un altro passo che devi fare e che sta salvando i file modificati.

Se stai utilizzando emacs versione 22 o successive, chiama ibuffer per accedere a una modalità di elenco buffer, quindi digita 【* u】 per contrassegnare tutti i file non salvati, quindi digita S per salvarli tutti. (sono i turni)

Se stai usando una versione 21 di emacs, puoi farlo: chiama i buffer della lista, quindi sposta il cursore sul file che vuoi salvare e digita s. Contrassegnerà il file per successive azioni di salvataggio. Digita u per deselezionare. Al termine, digitare x per eseguire il salvataggio di tutti i file contrassegnati per il salvataggio. (in emacs, il file aperto si chiama "buffer". Ignora le altre cose lì.)

In alternativa alle opzioni precedenti, puoi anche chiamare save-some-buffers 【Ctrl + xs】. Quindi emacs visualizzerà ogni file non salvato e chiederà se lo si desidera salvare.

Nota: regex di emacs non è lo stesso di Perl o Python, ma simile. Per un riepilogo e schemi comuni, vedi: Emacs Regex.


2
Questa risposta dovrebbe renderla più amichevole per i principianti rispetto alla risposta più votata che spero. Grazie per avermi motivato a mettere il tutto al posto del link.
Prashant Sharma,

La risposta inizia con la fonte: come prima riga. La logica per il copia e incolla è: a volte il collegamento esterno ai blog diventa obsoleto e quindi la risposta diventa inutile. E così come suggerito dalla meta wiki. Ho deciso di copiare tutto.
Prashant Sharma,

5

L'uso di dired per richiamare un albero di directory profondo sarà un po 'lento per questa attività. Si potrebbe considerare l'utilizzo di tag-query-replace . Ciò significa sborsare per creare una tabella di tag, ma ciò è spesso utile comunque ed è veloce.


Ho appena provato su una directory con oltre 14.000 file. Ci sono voluti alcuni secondi per far apparire popup in Emacs. Quindi sicuramente non lento (più).
Berend de Boer,

3

Per i buffer aperti, questo è quello che faccio:

(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 Direde t per contrassegnare tutti i file e Qper interrogare sostituire il testo in tutti. È possibile espandere una sottodirectory usando il icomando prima di sostituire la query. Le informazioni chiave che sto aggiungendo sono che se dai un prefisso (control-u) al comando i, ti chiederà arg, e l'argomento -R espanderà ricorsivamente tutto subdirs nel diredbuffer. Quindi ora puoi eseguire una query di ricerca di tutti i file in un'intera directory.


2

Un'altra opzione è quella di utilizzare la ricerca di ghiaccioli . Questo è un diverso tipo di ricerca incrementale che utilizza il completamento dell'input del minibuffer rispetto ai risultati della ricerca. Man mano che modifichi l'input corrente, il set di hit corrispondenti viene aggiornato nel buffer *Completions*.

È possibile cercare un numero qualsiasi di file, buffer o posizioni dei segnalibri, che è possibile scegliere utilizzando la corrispondenza del modello di minibuffer (ad esempio regexp).

Quando si visita un hit di ricerca, è possibile sostituire su richiesta l'intero hit o solo la parte di esso corrispondente all'input del minibuffer corrente. La sostituzione su richiesta significa che non si viene interrogati su ogni hit di ricerca a turno; accedi agli hit che desideri direttamente, in qualsiasi ordine. Questo approccio può essere più efficace di una query di sostituzione se si dispone di un numero limitato di sostituzioni: si salta la y/nrichiesta esaustiva .

La ricerca riguarda i contesti di ricerca definiti dall'utente - non si è limitati alla ricerca di tutto il testo nei file di destinazione (ad esempio, è possibile saltare commenti o particolari tipi di sezioni del programma). Un semplice esempio di un contesto di ricerca è una linea, come in grep, ma un contesto può essere qualsiasi blocco di testo corrispondente al modello che ti piace. In genere si definiscono i contesti di ricerca utilizzando una regexp, ma è invece possibile utilizzare una funzione. Oltre a definire il tuo, ci sono comandi di ricerca Icicles predefiniti per diversi tipi di contesti: blocchi di proprietà del testo o proprietà di overlay, cose a punto, ecc.

È inoltre possibile ordinare i risultati della ricerca in vari ordinamenti per facilitare l'accesso / la navigazione.


2

find-name-dired va bene, ma:

  • Tutti i file ottenuti corrispondono allo stesso regexp singolo.
  • find-diredè più flessibile a tale riguardo, ma è anche concepito per l'utilizzo di regole generali (anche se possono essere arbitrariamente complesse). E ovviamente findha un suo linguaggio complesso.
  • se si desidera quindi agire solo su alcuni dei file i cui nomi sono stati raccolti nel find(-name)-diredbuffer, è necessario contrassegnarli o eliminare / omettere le righe di quelli su cui non si desidera agire.

Un'alternativa è usare i comandi Dired + che agiscono su (a) i file contrassegnati e (b) tutti i file contrassegnati (o tutti i file, se non ne sono marcati) nelle sottodirectory contrassegnate ... trovati in modo ricorsivo . Questo ti dà sia generalità che facile controllo sulla scelta dei file. Questi comandi "qui-e-sotto" sono tutti sul tasto prefisso M-+in modalità Dired.

Ad esempio, M-+ Qè lo stesso di Q--- query-replace, ma i file di destinazione sono tutti quelli contrassegnati nella directory corrente e in tutti i sottodir, marcati, giù, giù, giù ...

Sì, un'alternativa all'utilizzo di tali comandi qui-e-sotto consiste nell'inserire tutti i sottodir e i loro sottodir, ricorsivamente, e quindi utilizzare un comando di livello superiore come Q. Ma spesso può essere conveniente non disturbare con i sottodirectory inseriti.

E per farlo hai comunque bisogno di un modo rapido per inserire ricorsivamente tutti questi sottodir . Anche qui, Dired + può aiutarti. M-+ M-iinserisce in modo ricorsivo tutti i sottodiretti contrassegnati e i loro sottodiretti contrassegnati. Cioè, è come M-i(che inserisce i sottodiretti contrassegnati in Dired + ), ma agisce in modo ricorsivo sui sottodir.

(Tutti questi "qui e sotto" comandi Dired + sono nel menu Multiplo > Contrassegnato qui e sotto .)

Puoi anche eseguire operazioni Dired su un Emacs set di file , che è un set salvato di nomi di file situati ovunque. E se usi Icicles puoi aprire un buffer Dired solo per i file in un set di file o altri tipi di elenchi di file salvati.

È inoltre possibile aggiungere un segnalibro a qualsiasi buffer Dired, incluso quello creato utilizzando find(-name)-dired. Questo ti dà un modo rapido per tornare a un tale set (ad esempio un set di progetti) in seguito. E se usi Bookmark +, aggiungi un segnalibro a un buffer Dired registra (a) i suoi lsswitch, (b) quali file sono contrassegnati, (c) quali sottodirectory sono inserite e (d) quali (sotto) directory sono nascoste. Tutto ciò viene ripristinato quando si "salta" al segnalibro. Bookmark + ti consente anche di aggiungere un intero albero di buffer Dired --- saltare al segnalibro ripristina tutti i buffer dell'albero.


Chiunque: ti interessa spiegare perché hai votato in negativo questa risposta?
Drew

1

Vorrei suggerire un altro grande strumento che non è stato ancora menzionato, ovvero Helm .

È un ottimo sostituto per molte operazioni Emacs standard che prevedono il completamento, la ricerca, ecc. In particolare, helm-find-files consente di eseguire la sostituzione di query (incluso regexp) all'interno di più file selezionati.

Basta aprire helm-find-files, contrassegnare i file pertinenti con M-SPCe quindi utilizzare F6o F7per eseguire la query di sostituzione o la query di sostituzione regexp nei file selezionati.


Può helm-find-filescontrassegnare con regexp anziché M-SPC?
Nordlöw,

-2

Non è Emacs, ma xxdiff viene fornito con uno strumento chiamato xx-rename che lo farà per più stringhe alla volta (ad es. Da A da a DA A), con prompt interattivi, salva i backup di tutti i file modificati e produce un breve registro delle modifiche apportate con il contesto. Questo è ciò che tendo a utilizzare quando eseguo rinominazioni di grandi dimensioni / globali.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.