Rechercher et remplacer dans Vim dans tous les fichiers du projet

117

Je cherche le meilleur moyen de rechercher et remplacer (avec confirmation) dans tous les fichiers de projet dans Vim. Par «fichiers de projet», j'entends les fichiers du répertoire courant, dont certains n'ont pas besoin d'être ouverts.

Une façon de faire pourrait être d'ouvrir simplement tous les fichiers du répertoire courant:

:args ./**

puis effectuez la recherche et le remplacement sur tous les fichiers ouverts:

:argdo %s/Search/Replace/gce

Cependant, lorsque je fais cela, l'utilisation de la mémoire de Vim passe de quelques dizaines de Mo à plus de 2 Go, ce qui ne fonctionne pas pour moi.

J'ai également installé le plugin EasyGrep , mais il ne fonctionne presque jamais - soit il ne trouve pas toutes les occurrences, soit il se bloque jusqu'à ce que j'appuie sur CtrlC. Jusqu'à présent, ma manière préférée d'accomplir cette tâche est d' ack-grep pour le terme de recherche, en utilisant sa fenêtre de correction rapide, ouvrez tout fichier contenant le terme et qui n'a pas été ouvert auparavant, et enfin :bufdo %s/Search/Replace/gce.

Je recherche soit un bon plugin fonctionnel qui puisse être utilisé pour cela, soit une commande / séquence de commandes qui serait plus facile que celle que j'utilise actuellement.

psyho
la source
1
@Cascabel Depuis que vous avez écrit ce commentaire, il existe un site vi.stackexchange.com.
Flimm

Réponses:

27

Greplace fonctionne bien pour moi.

Il existe également une version prête pour les agents pathogènes sur github.

tuxcanfly
la source
8
Installé, je vois les autres commandes comme :Gsearchet :Gbuffersearchexistent, mais quand je tape :Greplaceje reçois Not an editor command: Greplace.
Ramon Tayag le
selon la documentation, la syntaxe est la suivante: :Gsearch [<grep-option(s)>] [[<pattern>] [<filename(s)>]] vous pouvez donc faire une recherche récursive en utilisant, par exemple::Gsearch -r pattern dir/
vitorbal
Ce que je déteste à propos de Greplace, c'est que si le motif est dans le nom de fichier, Greplace échoue, car vous finissez par le remplacer également dans le nom de fichier.
Daniel
2
Rien d'inhabituel ici après 6 ans, mais ce plugin est gravement obsolète par rapport à certaines nouvelles réponses ( github.com/yegappan/greplace ) dernière mise à jour, il y a plus de 3 ans. Je pourrais consulter la solution intégrée de Sid: stackoverflow.com/a/38004355/2224331 (Ce n'est pas moi sur un autre compte, juste une coïncidence: P)
SidOfc
99

L'autre grande option ici est simplement de ne pas utiliser vim:

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

ou si vous avez un moyen de générer une liste de fichiers, peut-être quelque chose comme ceci:

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

etc!

Cascabel
la source
Merci! J'ai vraiment besoin d'apprendre ces trucs de ligne de commande à l'ancienne. Je pense que c'est la meilleure réponse. Est-ce que ces expressions régulières sont des expressions régulières perl ou des expressions régulières vim ou un autre type de regex?
Eric Johnson
2
@EricJohnson: Ce sont ... seddes expressions régulières, qui sont essentiellement des expressions régulières de base POSIX.2, mais pas exactement (c'est dans la page de manuel) - elles permettent de faire \ncorrespondre les nouvelles lignes, et d'autres choses similaires. Ils sont donc à peu près les mêmes que greples expressions régulières.
Cascabel
Y a-t-il une raison pour laquelle vous avez -i dans la commande sed? La page de manuel indique que c'est pour spécifier une extension, mais vous ne semblez pas en spécifier une.
davekaro
Ok, il ressemble en fait à 's / pattern / replacement /' est l'extension, je pense que je comprends maintenant.
davekaro
11
Sur Mac OS X, la seule commande sed qui a fonctionné pour moi était:find . -type f | LANG=C xargs sed -i '' 's/SEARCH/REPLACE/g'
ghodss
74

EDIT: utilisez la cfdocommande au lieu de cdopour réduire considérablement le nombre de commandes qui seront exécutées pour accomplir cela (car cdoexécute des commandes sur chaque élément tout en cfdoexécutant des commandes sur chaque fichier)

Grâce à la cdocommande récemment ajoutée , vous pouvez maintenant le faire en deux commandes simples en utilisant n'importe quel grepoutil que vous avez installé. Aucun plug-in supplémentaire requis !:

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

( cdoexécute la commande donnée pour chaque terme de la liste de correctifs rapides, que votre grepcommande remplit.)

(Supprimez le cà la fin de la 2ème commande si vous souhaitez remplacer chaque terme de recherche sans confirmer à chaque fois)

Sid
la source
12
De plus, vous pouvez ajouter :cdo updatepour enregistrer les modifications dans tous les fichiers.
Zhe Li
1
Mais il s'arrêtera pour avertir non enregistré à chaque fois avant de passer au tampon suivant lorsque le tampon actuel est modifié.
Gratuit
1
@Zhe merci, j'ai ajouté cela à la réponse; @lfree Je préfère personnellement confirmer chaque changement, mais vous pouvez supprimer le cà la fin de la deuxième commande (utilisez simplement g) pour le faire rechercher et remplacer sans demander de confirmation
Sid
1
: cdo% s / terme de recherche / remplacer le terme / g ne remplace que la première occurrence, mais ne fonctionne pas pour la deuxième occurrence dans mon vim
sunxd
3
pour l'utiliser update, je devais:cdo %s/<search term>/<replace term>/g | update
gabe
34

J'ai décidé d'utiliser ack et Perl pour résoudre ce problème afin de tirer parti des expressions régulières Perl complètes plus puissantes plutôt que du sous-ensemble GNU.

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

Explication

accuser

ack est un outil impressionnant de ligne de commande qui est un mélange de grep, findet plein d' expressions régulières Perl (pas seulement le sous - ensemble GNU). Il est écrit en pur Perl, rapide, il a la coloration syntaxique, fonctionne sous Windows et est plus convivial pour les programmeurs que les outils de ligne de commande traditionnels. Installez-le sur Ubuntu avec sudo apt-get install ack-grep.

xargs

Xargs est un ancien outil de ligne de commande unix. Il lit les éléments de l'entrée standard et exécute la commande spécifiée suivie des éléments lus pour l'entrée standard. Donc, fondamentalement, la liste des fichiers générés par ack est ajoutée à la fin de la perl -pi -E 's/pattern/replacemnt/g'commande.

perl -pi

Perl est un langage de programmation. L'option -p amène Perl à créer une boucle autour de votre programme qui itère sur les arguments de nom de fichier. L'option -i oblige Perl à éditer le fichier sur place. Vous pouvez le modifier pour créer des sauvegardes. L'option -E oblige Perl à exécuter la ligne de code spécifiée comme programme. Dans notre cas, le programme est juste une substitution de regex Perl. Pour plus d'informations sur les options de ligne de commande Perl perldoc perlrun. Pour plus d'informations sur Perl, consultez http://www.perl.org/ .

Eric Johnson
la source
J'aime cette réponse car elle fonctionne même avec les fins de ligne de cygwin. Notez qu'il ajoute un \ r à la fin des lignes modifiées, mais lors de l'utilisation de sed, il remplacera chaque nouvelle ligne, rendant vos diffs inutilisables. Perl est un peu plus sain d'esprit, et c'est un très bon one-liner pour la recherche et le remplacement. Merci!
andrew
@andrew, je ne sais pas exactement ce qui ne va pas dans votre cas. Cela vous aiderait-il à utiliser \ R au lieu de \ n dans vos expressions rationnelles? Voir la section \ R dans perldoc.perl.org/perlrebackslash.html#Misc . Essayez également perldoc.perl.org/perlre.html#Regular-Expressions . Perl est extrêmement multiplateforme et je suis sûr qu'il existe un moyen pour vous de ne pas vous retrouver avec des fins de ligne mutilées.
Eric Johnson
Mon regex ne contient aucune nouvelle ligne, les versions cygwin de ces programmes ajoutent automatiquement \ r à la fin de chaque ligne modifiée. J'ai particulièrement aimé la version perl car elle ne modifie que les lignes auxquelles elle correspond, tandis que sed modifiera toutes les lignes, même si elles ne correspondent pas à l'expression régulière.
andrew
Que faire si le chemin du fichier contient des espaces?
shengy
23

peut-être faire ceci:

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

Explication:

  • :noautocmd vim /Search/ **/*⇒ rechercher ( vimest une abréviation pour vimgrep) motif dans tous les fichiers de tous les sous-répertoires du cwd sans déclencher autocmds ( :noautocmd), pour des raisons de vitesse.
  • :set hidden ⇒ permettre d'avoir des tampons modifiés non affichés dans une fenêtre (pourrait être dans votre vimrc)
  • :cfirst ⇒ passer au premier résultat de recherche
  • qa ⇒ commencer l'enregistrement d'une macro dans le registre a
  • :%s//Replace/gce⇒ remplacer toutes les occurrences du dernier modèle de recherche (toujours /Search/à ce moment-là) par Replace:
    • plusieurs fois sur une même ligne ( gdrapeau)
    • avec confirmation de l'utilisateur ( cdrapeau)
    • sans erreur si aucun motif trouvé ( edrapeau)
  • :cnf⇒ passer au fichier suivant dans la liste créée par la vimcommande
  • q ⇒ arrêter l'enregistrement de la macro
  • 1000@a ⇒ jouer la macro stockée dans le registre 1000 fois
  • :wa ⇒ enregistrer tous les tampons modifiés

* EDIT * Voie Vim 8:

À partir de Vim 8, il existe une meilleure façon de le faire, car il se :cfdorépète sur tous les fichiers de la liste des correctifs rapides:

:noautocmd vim /Search/ **/*
:set hidden
:cfdo %s//Replace/gce
:wa
Benoit
la source
vous voudrez peut-être expliquer cela un peu plus loin pour nous, les mortels inférieurs. Ce n'est pas clair pour moi si vous devez faire cette "séquence" à chaque fois. Je le fais plus vite en utilisant mon vieux Delphi 5 rouillé, donc je dois sûrement manquer quelque chose.
Lieven Keersmaekers
1
@Lieven: Vous avez raison, c'est un peu obscur sans commentaires. Je vais développer quelques explications.
Benoit le
+1 mais même si cela vimm'a convaincu pour beaucoup de choses, je pense que je vais m'en tenir à PowerGrep avec cela.
Lieven Keersmaekers
Merci pour votre réponse et l'explication de toutes les commandes, je l'apprécie vraiment. J'accepte l'autre réponse car je préfère utiliser un plugin pour cela.
psyho
J'ai noté cela parce que cela cause plus de confusion, étant donné la disparité entre le niveau de quelqu'un qui pose la question et le niveau requis pour comprendre cette réponse.
Lloyd Moore
22

Remplir à :argspartir d'une commande shell

Il est possible (sur certains systèmes d'exploitation 1 )) de fournir les fichiers :argsvia une commande shell.

Par exemple, si vous avez installé ack 2 ,

:args `ack -l pattern`

demandera à ack de retourner une liste de fichiers contenant 'pattern' et de les mettre dans la liste d'arguments.

Ou avec un vieux grep ordinaire, je suppose que ce serait:

:args `grep -lr pattern .`  


Vous pouvez alors simplement utiliser :argdocomme décrit par l'OP:

:argdo %s/pattern/replacement/gce


Remplir à :argspartir de la liste des correctifs rapides

Consultez également la réponse de nelstrom à une question connexe décrivant une commande simple définie par l'utilisateur qui remplit l'arglist à partir de la liste de correctifs rapides actuelle. Cela fonctionne très bien avec de nombreuses commandes et plugins dont la sortie se termine dans la liste des correctifs rapides ( :vimgrep, :Ack3 , :Ggrep4 ).

La séquence pour effectuer une recherche à l'échelle du projet pourrait alors être effectuée avec:

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

:Qargsest l'appel à la commande définie par l'utilisateur qui remplit l'arglist à partir de la liste de correctifs rapides.

Vous trouverez également des liens dans la discussion qui suit vers des plugins simples qui réduisent ce flux de travail à 2 ou 3 commandes.

Liens

  1. : h {arglist} - vimdoc.sourceforge.net/htmldoc/editing.html#{arglist}
  2. ack - betterthangrep.com/
  3. ack.vim - github.com/mileszs/ack.vim
  4. fugitif - github.com/tpope/vim-fugitive
exbinaire
la source
1
argdo s'arrête après le premier fichier avec un problème d'écriture
frankster
9

Une autre option en 2016, le plugin far.vim :

far.vom

Oleg Khalidov
la source
4
Vous devez également dire que vous êtes l'auteur du plugin :)
sitilge
5

Si cela ne vous dérange pas d'introduire une dépendance externe, j'ai préparé un plugin ctrlsf.vim (dépend de ack ou ag ) pour faire le travail.

Il peut formater et afficher les résultats de la recherche depuis ack / ag, et synchroniser vos modifications dans le tampon de résultats avec les fichiers réels sur le disque.

Peut-être que la démo suivante explique plus

ctrlsf_edit_demo

dyng
la source
3
1. :grep <search term> (or whatever you use to populate the quickfix window)
2. :cfdo %s/<search term>/<replace term>/g | update

L'étape 1 remplit la liste des correctifs rapides avec les éléments souhaités. Dans ce cas, il est propagé avec les termes de recherche que vous souhaitez modifier via grep.

cfdoexécute la commande suivante sur chaque fichier de la liste des correctifs rapides. Tapez :help cfdopour plus de détails.

s/<search term>/<replace term>/gremplace chaque terme. /gsignifie remplacer chaque occurrence du fichier.

| update enregistre le fichier après chaque remplacement.

J'ai reconstitué cela sur la base de cette réponse et de ses commentaires, mais j'ai estimé qu'il méritait sa propre réponse car tout est au même endroit.

Kyle Heironimus
la source
Pour moi sur NeoVim, une légère modification a dû être faite dans la ligne 2 ci-dessus: 2. :cfdo %s/<search term>/<replace term>/g | updateSans le %, seule la première occurrence du motif est remplacée.
Kareem Ergawy
Merci Kareem. J'ai mis à jour la réponse car %sfonctionne également sur vim régulier.
Kyle Heironimus
0

Fondamentalement, je voulais le remplacement en une seule commande et surtout dans vim lui-même

Sur la base de la réponse de @Jefromi, j'ai créé un raccourci clavier, que j'avais défini dans mon fichier .vimrc comme ceci

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

maintenant à partir du vim, sur un coup de leader + r je reçois la commande chargée dans vim, que j'édite comme ci-dessous,

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

Par conséquent, je fais le remplacement avec une seule commande et, plus important encore, dans vim lui-même

deepak
la source
et j'utilise réellement ag au lieu de grep
deepak