Utilisation d'Emacs pour rechercher et remplacer récursivement des fichiers texte qui ne sont pas déjà ouverts

212

À la suite de cette question , il essaie de savoir comment faire quelque chose comme ça qui devrait être facile, ce qui m'empêche surtout de m'habituer à utiliser Emacs et de démarrer l'éditeur que je connais déjà. J'utilise l'exemple ici assez souvent pour éditer plusieurs fichiers.

Dans Ultraedit, je ferais Alt + s puis p pour afficher une boîte de dialogue avec les options: Rechercher (inclut l'utilisation d'expressions régulières sur plusieurs lignes), Remplacer par, Dans les fichiers / types, Répertoire, Faire correspondre la casse, Faire correspondre le mot entier uniquement, Liste Fichiers modifiés et sous-répertoires de recherche. Habituellement, je vais d'abord utiliser la souris pour cliquer-glisser sélectionner le texte que je veux remplacer.

En utilisant uniquement Emacs lui-même (sous Windows XP), sans appeler aucun utilitaire externe, comment remplacer tous les foo \ nbar par bar \ nbaz *.cet les *.hfichiers dans certains dossiers et tous les dossiers en dessous. Emacs n'est peut-être pas le meilleur outil pour le faire, mais comment le faire facilement avec une commande minimale?

Rob Kam
la source

Réponses:

370
  1. M-x find-name-dired: vous serez invité à entrer un répertoire racine et un modèle de nom de fichier.
  2. Appuyez sur tpour "basculer la marque" pour tous les fichiers trouvés.
  3. Appuyez Qsur "Query-Replace in Files ...": vous serez invité à indiquer des expressions rationnelles de requête / substitution.
  4. Procédez comme avec query-replace-regexp: SPACEpour remplacer et passer au match suivant, npour sauter un match, etc.
  5. Appuyez sur C-x spour enregistrer les tampons. (Vous pouvez ensuite appuyer sur y, nou !pour tout enregistrer en même temps)
Chris Conway
la source
10
Non, c'est récursif. La version non récursive serait% m en mode dired.
Chris Conway
5
Peut-être parce que vous appelez C: \ WINDOWS \ system32 \ find.exe, pas GNU find.
Jazz
6
Steen, Chris: Probablement pas exactement ce que vous voulez mais vous pouvez utiliser 'Cx s' (save-some-buffers), il vous demandera pour chaque fichier modifié, vous n'avez donc qu'à taper 'y' plusieurs fois.
danielpoe
48
C-x s !pour enregistrer tous les fichiers à la fois.
cYrus
10
M-,pour continuer la recherche et remplacer (par exemple après l'invite "restaurer le fichier" si le fichier a changé sur le disque).
tfischbach
37
  • M-x find-name-dired RET
    • cela peut prendre un certain temps pour que tous les fichiers apparaissent dans la liste, faites défiler vers le bas ( M->) jusqu'à ce que " find finished" apparaisse pour vous assurer qu'ils ont tous été chargés
  • Appuyez tpour "basculer la marque" pour tous les fichiers trouvés
  • Appuyez Qsur "Query-Replace in Files ...": vous serez invité à indiquer des expressions rationnelles de requête / substitution.
  • Procédez comme avec query-replace-regexp: SPACE ou ypour remplacer et passer à la correspondance suivante, n pour ignorer une correspondance, etc.
    • Tapez! pour remplacer toutes les occurrences dans le fichier en cours sans demander, Npour ignorer tout remplacement possible pour le reste du fichier en cours. ( Nest emacs 23+ uniquement)
    • Pour effectuer le remplacement de tous les fichiers sans demander plus, tapez Y.
  • Appelez «ibuffer» ( C-x C-bs'il est lié à ibuffer ou M-x ibuffer RET) pour répertorier tous les fichiers ouverts.
  • Tapez * upour marquer tous les fichiers non enregistrés, tapez Spour enregistrer tous les fichiers marqués
  • * * RETpour décocher toutes les marques, ou tapez Dpour fermer tous les fichiers marqués

Cette réponse est combinée à partir de cette réponse , de ce site et de mes propres notes. Utilisation d'Emacs 23+.

Frank Henard
la source
3
ibuffern'est pas lié C-x C-bpar défaut.
phils
2
Eh bien , personnellement , je recommande que les gens ne se lient C-x C-bà ibuffer, parce qu'il est tellement mieux que la liaison par défaut. Ajoutez simplement (global-set-key (kbd "C-x C-b") 'ibuffer)à votre fichier .emacs. A défaut, utilisez M-x ibuffer RETles instructions ci-dessus.
phils
D'accord. Je pense avoir le kit de démarrage emacs, c'est pourquoi le mien est lié
Frank Henard
1
Le fichier bla-bla-blaest visité en lecture seule et sa recherche s'arrête. Comment éviter / ignorer cela et continuer à remplacer? Merci.
Felipe
3
Pourquoi ibufferquand vous pouvez C-x s( save-some-buffers) et taper !? Je ne veux pas me plaindre, juste curieux. PS plus pour décrire !, Net Y.
d12frosted le
17

Les réponses fournies sont excellentes, mais j'ai pensé ajouter une approche légèrement différente.

C'est une méthode plus interactive, et nécessite wgrep, rgrepet iedit. Les deux ieditet wgrepdoivent être installés via MELPA ou Marmalade (en utilisant M-x package-list-packages)

Exécutez d'abord M-x rgreppour trouver la chaîne que vous recherchez.

Vous serez en mesure de spécifier les types / modèles de fichiers et le dossier à récupérer.

Ensuite, vous devrez exécuter wgrepdémarrer avec C-s C-p.

Wgrep vous permettra de modifier les rgreprésultats, donc définir une région sur la chaîne en fonction et commencer iedit-modeavec C-;( en fonction de votre terminal , vous devrez peut - être re-lier ce)

Toutes les occurrences seront modifiables en même temps. C-x C-sà s'engager wgrep. Ensuite, C-x s !pour enregistrer les fichiers modifiés.

Le principal avantage de cette méthode est que vous pouvez utiliser iedit-mode pour désactiver certaines correspondances M-;. Vous pouvez également utiliser les résultats dans rgreppour accéder aux fichiers, par exemple si vous avez une correspondance inattendue.

Je le trouve très utile pour effectuer des modifications de source et renommer des symboles (variables, noms de fonction, etc.) dans un projet.

Si vous ne connaissez pas / n'utilisez pas déjà le mode iedit, c'est un outil très pratique, je vous recommande fortement d'y jeter un œil.

ocodo
la source
Cette technique est assez puissante même sans utiliser iedit. Cela transforme savoir comment grep (facile avec lgrep / rgrep) en sachant comment remplacer en masse la recherche!
NeilenMarais
Combiner rgrep / wgrep / macros est génial.
ocodo
2
Le raccourci clavier pour commencer wgrepest C-c C-pbtw.
techniao
15

Le projectile est vraiment sympa: Cc pr exécute la commande projectile-replace

eflanigan00
la source
13

J'utilise généralement d'autres outils pour effectuer cette tâche, et il semble que la plupart des approches mentionnées dans l'entrée Find and Replace Across Files d' EmacsWiki se décomposent , mais le package Findr semble très prometteur.

Vol d'une partie du fichier source :

(defun findr-query-replace (from to name dir)
  "Do `query-replace-regexp' of FROM with TO, on each file found by findr.
Blair Conrad
la source
comment puis-je essayer de trouver la chaîne "Collectible" dans tous les fichiers avec l'extension java et hxx en utilisant findr.el?
swdev
@swdev: Mx findr-query-replace RET Collectible RET <le remplacement> RET. * \. java RET <le répertoire dans
lequel
J'aime cette approche (elle évite tous les basculements impliqués dans l'approche directe). J'utilise certaines fonctions pratiques pour combiner cela avec un projectile (project-root-discovery) et remplacer la chose au point: gist.github.com/anonymous/055a9d62f9fc824153599724b8f44d57
Att Righ
6

Source d'information: 1

Pour les utilisateurs d'emacs pro:

  1. Appelez dired pour répertorier les fichiers dans dir, ou appelez find-dired si vous avez besoin de tous les sous-répertoires.
  2. Marquez les fichiers que vous souhaitez. Vous pouvez marquer par expression régulière en tapant 【% m】.
  3. Tapez Q pour appeler dired-do-query-replace-regexp.
  4. Tapez votre expression régulière et remplacez la chaîne. 〔☛ motif regex elisp commun〕
  5. Pour chaque occurrence, tapez y pour remplacer, n pour ignorer. Tapez 【Ctrl + g】 pour abandonner toute l'opération.
  6. Tapez! pour remplacer toutes les occurrences dans le fichier en cours sans demander, N pour ignorer tout remplacement possible pour le reste du fichier en cours. (N est emacs 23 uniquement)
  7. Pour effectuer le remplacement de tous les fichiers sans demander plus, tapez Y. (Emacs 23 uniquement)
  8. Appelez ibuffer pour répertorier tous les fichiers ouverts. Tapez 【* u】 pour marquer tous les fichiers non enregistrés, tapez S pour enregistrer tous les fichiers marqués, tapez D pour les fermer tous.

Guide pas à pas pour les débutants Emacs

Sélectionnez les fichiers cibles

Démarrez emacs en tapant «emacs» dans l'invite d'interface de ligne de commande. (Ou, double-cliquez sur l'icône Emacs si vous êtes dans un environnement d'interface utilisateur graphique)

Sélection de fichiers dans un répertoire

Vous devez d'abord sélectionner les fichiers dont vous souhaitez effectuer le remplacement. Utilisez le menu graphique 〖Fichier ▸ Open Directory〗. Emacs vous demandera un chemin de répertoire. Tapez le chemin du répertoire, puis appuyez sur Entrée.

Maintenant, vous verrez la liste des fichiers, et maintenant vous devez marquer les fichiers sur lesquels la recherche / remplacement d'expressions régulières doit fonctionner. Vous marquez un fichier en déplaçant le curseur sur le fichier souhaité, puis appuyez sur m. Décochez-la en appuyant sur u. (Pour répertorier les sous-répertoires, déplacez votre curseur vers le répertoire et appuyez sur i. Le contenu du sous-répertoire sera répertorié en bas.) Pour marquer tous les fichiers par une expression régulière, tapez 【% m】, puis tapez votre modèle d'expression régulière. Par exemple, si vous souhaitez marquer tous les fichiers HTML, tapez 【% m】 puis .html $. (Vous pouvez trouver une liste des commandes de marquage dans le menu graphique "Mark" (ce menu apparaît lorsque vous êtes en mode Dired).)

Sélection de fichiers dans un répertoire et tous ses sous-répertoires

Si vous souhaitez rechercher / remplacer des fichiers dans un répertoire, y compris des centaines de sous-répertoires, voici une méthode pour sélectionner tous ces fichiers.

Appelez find-dired. (vous appelez une commande en appuyant sur 【Alt + x】) Tapez ensuite un nom de répertoire, ⁖ / Users / mary / myfiles

Remarque: si vous utilisez emacs sur un terminal texte non graphique unix et si 【Alt + x】 ne fonctionne pas, le trait de touche équivalent est 【Esc x】.

Emacs vous demandera avec l'invite "Run find (with args):". Si vous devez effectuer le remplacement sur tous les fichiers HTML, tapez -name "* html". Si vous ne vous souciez pas du type de fichier mais simplement de tous les fichiers sous ce répertoire, donnez «-type f».

Maintenant, marquez les fichiers comme décrit ci-dessus.

Recherche / remplacement interactif

Maintenant, vous êtes prêt à remplacer la recherche interactive. Par souci de simplicité, disons que vous souhaitez simplement remplacer le mot «rapide» par «super». Maintenant, appelez dired-do-query-replace-regexp. Il vous demandera la chaîne d'expression régulière et la chaîne de remplacement. Tapez "rapide", entrez, puis "super".

Maintenant, emacs utilisera votre modèle et vérifiera les fichiers, et s'arrêtera et vous montrera chaque fois qu'une correspondance se produira. Lorsque cela se produit, emacs vous le demandera et vous aurez le choix de faire le changement ou de l'ignorer. Pour effectuer la modification, tapez y. Pour ignorer, tapez n. Si vous souhaitez simplement qu'emacs aille de l'avant et apporte toutes ces modifications au fichier actuel, tapez!.

Si vous souhaitez annuler toute l'opération sans enregistrer les modifications que vous avez apportées, tapez 【Ctrl + g】, puis quittez emacs à l'aide du menu 〖Fichier ▸ Quitter Emacs〗.

Enregistrement des fichiers modifiés

Maintenant, après avoir traversé l'épreuve ci-dessus, il vous reste une étape à faire, à savoir l'enregistrement des fichiers modifiés.

Si vous utilisez emacs version 22 ou ultérieure, appelez ibuffer pour passer en mode de liste de tampons, puis tapez 【* u】 pour marquer tous les fichiers non enregistrés, puis tapez S pour les enregistrer tous. (c'est shift-s)

Si vous utilisez une version 21 d'emacs, vous pouvez le faire: appeler les tampons de liste, puis déplacez le curseur sur le fichier que vous souhaitez enregistrer et tapez s. Il marquera le fichier pour une sauvegarde ultérieure. Tapez u pour décocher. Une fois que vous avez terminé, tapez x pour exécuter l'enregistrement de tous les fichiers marqués pour l'enregistrement. (dans emacs, le fichier ouvert est appelé «tampon». Ne tenez pas compte des autres éléments.)

En alternative aux options ci-dessus, vous pouvez également appeler save-some-buffers 【Ctrl + xs】. Emacs affichera ensuite chaque fichier non enregistré et vous demandera si vous souhaitez le sauvegarder.

Remarque: l'expression régulière d'emacs n'est pas la même que Perl ou Python, mais similaire. Pour un résumé et des modèles courants, voir: Emacs Regex.

Prashant Sharma
la source
2
Cette réponse devrait le rendre plus convivial pour les novices que la réponse la plus votée que j'espère. Merci d'ailleurs de m'avoir motivé à mettre le tout au lieu du lien.
Prashant Sharma
La réponse commence par source: comme première ligne. La justification du copier-coller est: parfois, les liens externes vers les blogs deviennent obsolètes et la réponse sera alors inutile. Et donc comme suggéré par la méta wiki. J'ai décidé de copier le tout.
Prashant Sharma
5

L'utilisation de dired pour récupérer une arborescence de répertoires approfondie sera un peu lente pour cette tâche. Vous pourriez envisager d'utiliser tags-query-replace . Cela signifie qu'il faut décortiquer pour créer une table de balises, mais cela est souvent utile de toute façon, et c'est rapide.

Alex Coventry
la source
Je viens d'essayer un répertoire avec plus de 14 000 fichiers. Il a fallu quelques secondes pour faire apparaître Emacs. Donc certainement plus lent (plus).
Berend de Boer
3

Pour les tampons ouverts, voici ce que je fais:

(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)))))
yPhil
la source
3

M-X Diredet t pour marquer tous les fichiers et Qpour interroger remplacer le texte dans chacun d'eux. Vous pouvez développer un sous-répertoire en utilisant la icommande avant le remplacement de la requête. L'information clé que j'ajoute est que si vous donnez un préfixe (control-u) à la commande i, il vous demandera arg, et l'argument -R développera récursivement tout subdirs dans le diredtampon. Alors maintenant, vous pouvez rechercher-rechercher chaque fichier dans un répertoire entier.

Zack Murray
la source
2

Une autre option consiste à utiliser la recherche Icicles . Il s'agit d'un type différent de recherche incrémentielle qui utilise l'achèvement de votre entrée de mini-tampon contre les résultats de recherche. Lorsque vous modifiez votre entrée actuelle, l'ensemble des correspondances est mis à jour dans le tampon*Completions* .

Vous pouvez rechercher n'importe quel nombre de fichiers, de tampons ou d'emplacements marqués, que vous pouvez choisir en utilisant une correspondance de modèle de mini-tampon (par exemple regexp).

Lorsque vous visitez un hit de recherche, vous pouvez remplacer à la demande l'intégralité du hit ou seulement la partie qui correspond à votre entrée de mini-tampon actuelle. Le remplacement à la demande signifie que vous n'êtes pas interrogé à tour de rôle sur chaque recherche; vous accédez directement aux hits que vous souhaitez, dans n'importe quel ordre. Cette approche peut être plus efficace que le remplacement de requête si vous avez un nombre limité de remplacements à effectuer: vous ignorez l' y/ninvite exhaustive .

La recherche porte sur des contextes de recherche que vous définissez - vous n'êtes pas limité à rechercher tout le texte dans les fichiers cibles (par exemple, vous pouvez ignorer des commentaires ou des types particuliers de sections de programme). Un exemple simple d'un contexte de recherche est une ligne, comme dans grep, mais un contexte peut être n'importe quel bloc de texte correspondant à un motif que vous aimez. En règle générale, vous définissez les contextes de recherche à l'aide d'une expression rationnelle, mais vous pouvez plutôt utiliser une fonction. En plus de définir le vôtre, il existe des commandes de recherche Icicles prédéfinies pour différents types de contextes: blocs de propriétés de texte ou propriétés de superposition, choses ponctuelles, etc.

Vous pouvez également trier les résultats de recherche dans différents ordres de tri pour un accès / navigation plus facile.

A dessiné
la source
2

find-name-dired est OK, mais:

  • Tous les fichiers que vous obtenez correspondent à la même expression rationnelle unique.
  • find-diredest plus flexible à cet égard, mais il est également conçu pour l'utilisation de règles générales (même si elles peuvent être arbitrairement complexes). Et bien sûr, finda son propre langage complexe.
  • si vous souhaitez ensuite agir uniquement sur certains des fichiers dont les noms ont été collectés dans le find(-name)-diredtampon, vous devez soit les marquer, soit supprimer / omettre les lignes de ceux sur lesquels vous ne souhaitez pas agir.

Une alternative consiste à utiliser les commandes Dired + qui agissent sur (a) les fichiers marqués et (b) tous les fichiers marqués (ou tous les fichiers, si aucun n'est marqué) dans les sous-répertoires marqués ... trouvés récursivement . Cela vous donne à la fois une généralité et un contrôle facile sur le choix des fichiers. Ces commandes "ici et ci-dessous" sont toutes sur la touche de préfixe M-+en mode Dired.

Par exemple, M-+ Qc'est la même chose que Q--- query-replace, mais les fichiers cibles sont tous ceux marqués dans le répertoire courant et dans tous les sous-répertoires marqués, bas, bas, bas ...

Oui, une alternative à l'utilisation de ces commandes ci-dessous consiste à insérer tous les sous-répertoires et leurs sous-répertoires, de manière récursive, puis à utiliser une commande de niveau supérieur telle que Q. Mais il peut souvent être pratique de ne pas se soucier des sous-répertoires insérés.

Et pour ce faire, vous avez quand même besoin d'un moyen rapide d' insérer tous ces sous-répertoires de manière récursive . Ici aussi, Dired + peut vous aider. M-+ M-iinsère récursivement tous les sous-répertoires marqués et leurs propres sous-répertoires marqués. Autrement dit, c'est comme M-i(qui insère les sous-répertoires marqués dans Dired + ), mais il agit récursivement sur les sous-répertoires.

(Toutes ces commandes "ici et ci-dessous" Dired + sont dans le menu Multiple > Marqué ici et ci-dessous .)

Vous pouvez également effectuer des opérations DIRED sur un Emacs fileset , qui est un ensemble enregistré des noms de fichiers situés partout. Et si vous utilisez Icicles, vous pouvez ouvrir un tampon Dired uniquement pour les fichiers d'un ensemble de fichiers ou d'autres types de listes de fichiers enregistrés.

Vous pouvez également mettre en signet n'importe quel tampon Dired, y compris celui que vous créez à l'aide find(-name)-dired. Cela vous permet de revenir rapidement à un tel ensemble (par exemple un ensemble de projets) plus tard. Et si vous utilisez Bookmark +, le signet d'un tampon Dired enregistre (a) ses lscommutateurs, (b) quels fichiers sont marqués, (c) quels sous-répertoires sont insérés et (d) quels (sous) répertoires sont cachés. Tout cela est restauré lorsque vous "sautez" vers le signet. Signet + vous permet également de mettre en signet un arbre entier de tampons fatigués --- le saut vers le signet restaure tous les tampons de l'arbre.

A dessiné
la source
Qui que ce soit: Prenez soin d'expliquer pourquoi vous avez rejeté cette réponse?
Drew
1

Je voudrais suggérer un autre excellent outil qui n'a pas encore été mentionné, à savoir Helm .

C'est un excellent remplacement pour de nombreuses opérations Emacs standard impliquant la complétion, la recherche, etc. En particulier, helm-find-filespermet d'effectuer le remplacement de requête (y compris l'expression régulière) dans plusieurs fichiers sélectionnés.

Il suffit d'ouvrir helm-find-files, de marquer les fichiers appropriés avec M-SPC, puis d'utiliser F6ou F7pour exécuter la requête de remplacement ou la requête de remplacement de l'expression rationnelle dans les fichiers sélectionnés.

Andrzej Pronobis
la source
Peut helm-find-filesmarquer par regexp au lieu de M-SPC?
Nordlöw du
-2

Ce n'est pas Emacs, mais xxdiff est livré avec un outil appelé xx-rename qui le fera pour plusieurs chaînes à la fois (par exemple de à de de à de à), avec des invites interactives, enregistrez les sauvegardes de tous les fichiers modifiés et produisez un court journal des modifications apportées au contexte. C'est ce que j'ai tendance à utiliser lorsque je fais des renommages de grande taille / globaux.

blais
la source