web-dev-qa-db-fra.com

Utiliser Emacs pour rechercher et remplacer de manière récursive des fichiers texte non encore ouverts

En guise de suivi de cette question , il cherche à savoir comment faire quelque chose comme cela, ce qui devrait être facile, cela m'empêche surtout de m'habituer davantage à utiliser Emacs et de démarrer à la place de l'éditeur I suis déjà familier avec. J'utilise souvent l'exemple ici pour l'édition de plusieurs fichiers.

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

En utilisant uniquement Emacs (sous Windows XP), sans appeler d’utilitaire externe, comment remplacer tous les foo\nbar par bar\nbaz dans *.c et *.h fichiers dans certains dossiers et tous les dossiers situés en dessous. Peut-être qu'Emacs n'est pas le meilleur outil pour le faire, mais comment le faire facilement avec une commande minimale?

204
Rob Kam
  1. M-x find-name-dired: vous serez invité à entrer un répertoire racine et un motif de nom de fichier.
  2. Appuyez sur t pour "changer de marque" pour tous les fichiers trouvés.
  3. Appuyez sur Q pour "Query-Replace in Files ...": vous serez invité à entrer une expression rationnelle de requête/substitution.
  4. Procédez comme avec query-replace-regexp: SPACE pour remplacer et passer à la correspondance suivante, n pour ignorer une correspondance, etc.
  5. Presse C-x s pour sauvegarder les tampons. (Vous pouvez ensuite appuyer sur y, n ou ! pour tout sauvegarder en même temps)
351
Chris Conway
  • M-x find-name-dired RET
    • l’apparition de tous les fichiers dans la liste peut prendre un certain temps. Faites défiler vers le bas (M->) jusqu'à ce que "find finished "apparaît pour s’assurer qu’ils ont tous été chargés
  • Appuyez sur t pour "activer/désactiver" tous les fichiers trouvés.
  • Appuyez sur Q pour "Query-Replace in Files ...": vous serez invité à entrer une expression rationnelle de requête/substitution.
  • Procédez comme avec query-replace-regexp: SPACE ou y pour remplacer et passer à la correspondance suivante, n pour ignorer une correspondance, etc.
    • Tapez! pour remplacer toutes les occurrences dans le fichier actuel sans demander, N pour ignorer tous les remplacements possibles pour le reste du fichier actuel. (N est seulement emacs 23+)
    • Pour effectuer le remplacement de tous les fichiers sans demander plus, tapez Y.
  • Appelez “ibuffer” (C-x C-b si lié à ibuffer ou M-x ibuffer RET) pour lister tous les fichiers ouverts.
  • Tapez * u pour marquer tous les fichiers non sauvegardés, tapez S pour sauvegarder tous les fichiers marqués
  • * * RET pour supprimer toutes les marques, ou tapez D pour fermer tous les fichiers marqués

Cette réponse est combinée depuis cette réponse , depuis ce site et avec mes propres notes. Utiliser Emacs 23+.

36
Frank Henard

J'utilise généralement d'autres outils pour effectuer cette tâche, et il semble que beaucoup des approches mentionnées à entrées Rechercher et remplacer parmi les fichiers d'EmacsWiki Shell out, mais le Findr Package a l'air très prometteur.

Voler une partie de le fichier source :

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

Les réponses fournies sont excellentes, mais je pensais ajouter une approche légèrement différente.

C'est une méthode plus interactive qui nécessite wgrep, rgrep et iedit. iedit et wgrep doivent être installés via MELPA ou Marmalade (à l'aide de M-x package-list-packages)

Première exécution M-x rgrep pour trouver la chaîne que vous recherchez.

Vous serez en mesure de spécifier les types de fichier/modèle et le dossier à renvoyer.

Ensuite, vous devrez exécuter wgrep le démarrer avec C-s C-p.

Wgrep vous permettra d'éditer les résultats rgrep, donc définissez une région sur la chaîne pour qu'elle corresponde et démarrez iedit-mode avec C-; _ (en fonction de votre terminal, vous devrez peut-être redéfinir la liaison)

Toutes les occurrences seront modifiables à la fois. C-x C-s pour valider 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 de rgrep pour accéder aux fichiers, par exemple si vous avez une correspondance inattendue.

Je trouve cela très utile pour modifier les sources et renommer des symboles (variables, noms de fonctions, etc.) dans un projet.

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

12
ocodo

Le projectile est vraiment gentil: C-C p r exécute la commande projectile-replace

9
eflanigan00

L'utilisation de dired pour redéfinir une arborescence de répertoires profonde sera un peu lente pour cette tâche. Vous pourriez envisager d'utiliser tags-query-replace . Cela veut dire qu'il faut créer une table de balises, mais c'est quand même utile, et c'est rapide.

5
Alex Coventry

Source d'information: 1

Pour les utilisateurs d’emacs pro:

  1. Appelez dired pour lister les fichiers dans dir ou appelez find-dired si vous avez besoin de tous les sous-répertoires.
  2. Marquez les fichiers que vous voulez. Vous pouvez marquer par regex en tapant% m】.
  3. Tapez Q pour appeler dired-do-query-replace-regexp.
  4. Tapez votre expression rationnelle et remplacez la chaîne. Pattern ☛ modèle de 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 actuel sans demander, N pour ignorer tous les remplacements possibles pour le reste du fichier actuel. (N est seulement emacs 23)
  7. Pour effectuer le remplacement de tous les fichiers sans demander plus, tapez Y. (Emacs 23 uniquement)
  8. Appelez ibuffer pour lister tous les fichiers ouverts. Tapez 【* u pour marquer tous les fichiers non sauvegardés, tapez S pour sauvegarder tous les fichiers marqués, tapez D pour tous les fermer.

Guide étape par étape pour les débutants d'Emacs

Sélectionner les fichiers cibles

Démarrez emacs en tapant "emacs" dans l'invite de l'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 à remplacer. Utilisez le menu graphique 〖Fichier Ouvrir le répertoire. Emacs vous demandera un chemin de répertoire. Tapez le chemin du répertoire, puis appuyez sur Entrée.

Maintenant, la liste des fichiers s'affichera et vous devez maintenant marquer les fichiers sur lesquels vous souhaitez que la fonction regex find/replace fonctionne. Vous marquez un fichier en déplaçant le curseur sur le fichier souhaité, puis appuyez sur m. Désélectionnez-le en appuyant sur u. (Pour répertorier les sous-répertoires, déplacez votre curseur sur le répertoire et appuyez sur i. Le contenu du sous-répertoire est répertorié en bas.) Pour marquer tous les fichiers avec une expression régulière, tapez 【% m, puis tapez votre motif 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 "Marquer" (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.

Appel trouver-dired. (vous appelez une commande en appuyant sur 【Alt + x) Ensuite, tapez 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, la frappe équivalente est is Esc x 【.

Emacs vous demandera avec l'invite "Run find (with args):". Si vous devez remplacer tous les fichiers HTML, tapez -name "* html". Si vous ne vous souciez pas de quel 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.

Interactif Rechercher/Remplacer

Vous êtes maintenant prêt à remplacer la recherche interactive. Par souci de simplicité, supposons que vous souhaitiez simplement remplacer le mot "rapide" par "super". Maintenant, appelez dired-do-query-replace-regexp. Il vous demandera la chaîne regex et la chaîne de remplacement. Tapez "rapide", entrez, puis "super".

À présent, emacs utilisera votre modèle pour vérifier les fichiers et s’arrêtera pour vous montrer chaque correspondance. Lorsque cela se produit, emacs vous y invite, et vous avez le choix d’effectuer le changement ou de le sauter. Pour effectuer le changement, tapez y. Pour sauter, tapez n. Si vous voulez 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.

Sauvegarde des fichiers modifiés

Maintenant, après avoir traversé l'épreuve ci-dessus, il reste une étape: enregistrer les fichiers modifiés.

Si vous utilisez emacs version 22 ou ultérieure, appelez ibuffer pour passer en mode liste de mémoire tampon, puis tapez * u pour marquer tous les fichiers non sauvegardés, puis S pour tous les sauvegarder. (c'est shift-s)

Si vous utilisez une version 21 d’Emacs, procédez comme suit: appelez list-buffers, puis déplacez le curseur sur le fichier que vous souhaitez enregistrer et tapez s. Il marquera le fichier pour une action de sauvegarde ultérieure. Tapez u pour annuler le marquage. Une fois que vous avez terminé, tapez x pour exécuter la sauvegarde de tous les fichiers marqués pour la sauvegarde. (Dans Emacs, le fichier ouvert est appelé "tampon". Ne tenez pas compte d'autres éléments.)

En plus des options ci-dessus, vous pouvez également appeler save-some-buffers 【Ctrl + x s】. Ensuite, emacs affichera chaque fichier non enregistré et vous demandera si vous souhaitez l’enregistrer.

Remarque: l'expression régulière d'emacs n'est pas identique à celle de Perl ou de Python, mais similaire. Pour un résumé et des modèles communs, voir: Emacs Regex.

4
scrapcodes

M-X Dired, et t pour marquer tous les fichiers, et Q pour interroger le texte de remplacement dans chacun d’eux. Vous pouvez développer un sous-répertoire en utilisant la commande i avant la requête-remplacer. Les informations clés que j'ajoute sont que si vous donnez un préfixe (control-u) à la commande i, il vous invitera à argumenter, et l'argument -R développera récursivement tous subdirs dans le dired tampon. Alors maintenant, vous pouvez interroger chaque fichier dans un répertoire complet.

3
Zack Murray

Pour les buffers 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)))))
3
yPhil

Une autre option consiste à utiliser recherche de glaçons . Il s'agit d'un type de recherche incrémentielle différent qui utilise l'achèvement de la saisie de votre mini-tampon par rapport aux résultats de recherche. Lorsque vous modifiez votre entrée actuelle, le jeu de résultats correspondants est mis à jour dans la mémoire tampon *Completions*.

Vous pouvez rechercher un nombre quelconque de fichiers, tampons ou emplacements marqués, que vous pouvez choisir en utilisant un motif de mini-tampon (par exemple, regexp) correspondant.

Lorsque vous visitez un résultat de recherche, vous pouvez remplacer à la demande du résultat complet ou de la partie de celle-ci qui correspond à votre saisie de mini-tampon actuelle. Le remplacement à la demande signifie que vous n'êtes pas interrogé sur chaque résultat de recherche; vous accédez directement aux hits que vous voulez, dans n'importe quel ordre. Cette approche peut être plus efficace que requête-remplacement si vous avez un nombre limité de remplacements à effectuer: vous sautez l'exhaustif y/n invite.

La recherche est terminée sur la recherche des contextes que vous définissez - vous n'êtes pas limité à la recherche de tout le texte dans les fichiers cibles (par exemple, vous pouvez ignorer les commentaires ou types particuliers de sections de programme). Un exemple simple de contexte de recherche est une ligne, comme dans grep, mais un contexte peut être n’importe quel bloc de texte recherché qui vous ressemble. En règle générale, vous définissez les contextes de recherche à l'aide d'une expression rationnelle, mais vous pouvez également utiliser une fonction. En plus de définir les vôtres, 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 simples, etc.

Vous pouvez également trier les résultats de la recherche dans divers ordres de tri pour faciliter l'accès/la navigation.

1
Drew

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

C’est un excellent substitut à de nombreuses opérations Emacs standard telles que l’achèvement, la recherche, etc. En particulier, helm-find-files permet d'effectuer un remplacement de requête (y compris regexp) dans plusieurs fichiers sélectionnés.

Il suffit d'ouvrir helm-find-files, marquez les fichiers pertinents avec M-SPC puis utilisez F6 ou F7 pour exécuter la requête replace ou query replace regexp dans les fichiers sélectionnés.

1
Andrzej Pronobis

find-name-dired Est OK, mais:

  • Tous les fichiers que vous obtenez correspondent aux mêmes expressions simples.
  • find-dired Est plus flexible à cet égard, mais il est également conçu pour utiliser des règles générales (même si elles peuvent être arbitrairement complexes). Et bien sûr, find a son propre langage complexe.
  • si vous souhaitez ensuite agir uniquement sur certains des fichiers dont les noms ont été collectés dans la mémoire tampon find(-name)-dired, vous devez les marquer ou supprimer/supprimer les lignes de ceux sur lesquels vous ne souhaitez pas agir.

Une alternative consiste à utiliser Dired + commandes agissant 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 clé de préfixe M-+ en mode désactivé.

Par exemple, M-+ Q est identique à Q --- query-replace, mais les fichiers cibles sont tous de ceux marqués dans le répertoire courant et dans les éventuels sous-répertoires marqués, bas, bas, bas ...

Oui, une alternative à l'utilisation de telles commandes ici-et-dessous est de insérer tous les sous-répertoires et leurs sous-répertoires, de manière récursive, puis d'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 de toute façon besoin d'un moyen rapide pour insérer tous ces sous-répertoires de manière récursive. Ici aussi, Dired + peut aider. M-+ M-i insère tous les sous-répertoires marqués et leurs propres sous-répertoires marqués, de manière récursive. C'est-à-dire que cela ressemble à M-i (Qui insère les sous-répertoires marqués dans Dired + ), mais agit de manière récursive sur les sous-répertoires.

(Toutes ces commandes "ici-et-dessous" Dired + sont au menu multiples > marqué ici et ci-dessous .)

Vous pouvez également effectuer des opérations Dired sur un Emacs ensemble de fichiers, qui est un ensemble enregistré de noms de fichiers situés n'importe où. Et si vous utilisez Icicles , alors vous pouvez ouvrir un tampon Dired pour seulement les fichiers dans un ensemble de fichiers ou d'autres types de listes de fichiers enregistrés.

Vous pouvez également mettre en signet tout tampon Dired, y compris celui que vous avez créé à l'aide de find(-name)-dired. Cela vous donne un moyen rapide de revenir ultérieurement à un tel ensemble (par exemple, un ensemble de projets). Et si vous utilisez Signet + , alors enregistrez un tampon Dired enregistre (a) son ls commutateurs, (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" sur le signet. Signet + vous permet également de mettre en signet toute une arborescence de tampons Dired - le fait de sauter au signet restaure tous les tampons de l'arborescence.

1
Drew