Rechercher et remplacer à l'aide d'expressions régulières


26

J'ai un fichier avec un tas de valeurs par défaut de l'utilisateur. Je veux changer une partie du texte, mais j'ai du mal à trouver un matcher et un remplaçant. En utilisant l'exemple suivant:

###############################################################################
# Trackpad, mouse, keyboard, Bluetooth accessories, and input                 #
###############################################################################

# Trackpad: enable tap to click for this user and for the login screen
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

Je voudrais remplacer # Trackpad: ...parrunning "Trackpad: ..."

En décomposant le problème, j'ai trouvé quelque chose en utilisant un testeur d'expressions régulières:

/\n\n#\s(.*)/g

Si j'essaie de l'utiliser dans Vim, cela ne fonctionne pas pour moi:

:/\n\n#\s(.*)/running "\1"/g

Je suppose que mon problème se résume à deux questions spécifiques:

  1. Comment puis-je éviter de rechercher des \ncaractères et m'assurer à la place de #ne pas apparaître à la fin du groupe de recherche?
  2. Comment puis-je utiliser efficacement les groupes de capture?

Il y a quelques bonnes réponses ci-dessous. Difficile de choisir entre les trois, mais je pense que la réponse choisie est la plus précise pour ma spécification d'origine. Je vous recommande d'essayer les trois réponses avec le fichier réel pour voir ce que vous en pensez.

Réponses:


18

Juste pour être clair… Je crois que vous avez demandé que ce soit le résultat de la substitution?

###############################################################################
# Trackpad, mouse, keyboard, Bluetooth accessories, and input                 #
###############################################################################

running "Trackpad: enable tap to click for this user and for the login screen"
defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true

Dans ce cas, je recommande la commande suivante:

:%s/\n\n#\s\+\(.*\)/^M^Mrunning "\1"/

Explication du motif

:s/PATTERN/REPLACEMENT/est la commande de substitution . Le pourcentage de connexion le :%sfait fonctionner sur l'ensemble du fichier, plutôt que sur la ligne actuelle.

Le \n\ndit que la ligne d'intérêt doit apparaître après une ligne vierge. Si vous ne vous souciez pas de la ligne vide précédente, alors ^cela suffira.

#\s\+correspond à un caractère de hachage suivi d'un ou plusieurs espaces. \(.*\)capture tout le texte suivant sur la ligne.

Explication du texte de remplacement

^M^Minsère deux extrémités de lignes pour remplacer celles \n\nqui étaient présentes dans le motif. Sinon, le texte serait déplacé à la fin de la ligne précédant la ligne vierge. Pour saisir chacun ^M, appuyez sur Ctrl-V Ctrl-M.

Ensuite, insérez la chaîne running, suivie de tout ce qui a été capturé entre parenthèses entre guillemets doubles.


Je ne suis pas en mesure de modifier votre réponse, mais je pense que vous vouliez dire :%s/\v\n\n#\s+(.*)/^M^Mrunning "\1"/(ajouté le drapeau "magique"). Il est vraiment difficile de choisir une réponse correcte pour ma question d'origine, mais je pense que cette réponse est la plus proche de ma réponse attendue d'origine. C'est aussi le seul qui fonctionne dans tout le fichier sans avoir besoin de sélectionner une plage.
squarefrog

1
IIRC pouvez-vous utiliser \rau lieu de ^Mpour obtenir de nouvelles lignes?
muru

11

J'utiliserais quelque chose comme:

:s/^#\s\+\(.\{-}\):/running "\1":/
  • ^#pour correspondre au #caractère ancré au début de la ligne (cela répond à la question 1)
  • \s\+ pour correspondre à un ou plusieurs espaces blancs
  • \( pour démarrer un groupe (cela répond à la question 2)
  • .\{-}\pour faire correspondre n'importe quel caractère 0 fois ou plus d'une manière non gourmande ; cela diffère du .*fait qu'il essaie de correspondre le moins possible et pas autant que possible. Essayez d'ajouter un :caractère dans le commentaire pour voir pourquoi cela est important
  • \) pour terminer le sous-groupe.
  • : correspond à un littéral :

Nous remplaçons ensuite cela par le texte que vous voulez et utilisons \1pour faire référence au groupe que nous avons capturé.

J'ai trouvé quelque chose en utilisant un testeur d'expressions rationnelles

La syntaxe des expressions régulières est un peu comme la syntaxe wiki: il y en a beaucoup, elles se ressemblent toutes en un coup d'œil, aucune n'est évidemment meilleure qu'une autre, mais il y a beaucoup de différences.

Aujourd'hui, les expressions régulières dites "compatibles Perl" sont par défaut de facto dans la plupart des langues, mais les expressions régulières Vim ne sont pas compatibles avec les expressions compatibles Perl! La syntaxe d'expression rationnelle de Vim remonte au moins aux années 70, quand Perl n'était même pas là.

Vous pouvez le voir avec les sous-groupes, où vous devez utiliser \(et non ((cela est compatible avec la syntaxe POSIX "de base", mais pas avec la syntaxe POSIX "étendue" plus courante ou la syntaxe Perl). Vous pouvez contrôler cela en ajoutant le \vdrapeau dans un modèle (voir :help /\vpour plus de détails), cela le rendra "plus compatible", mais pas complètement (vous devez toujours l'utiliser .{-}pour des correspondances non gourmandes par exemple)

Cela pourrait donc expliquer pourquoi l'utilisation d'un "testeur d'expressions rationnelles" fonctionne presque, mais pas tout à fait,.

http://www.vimregex.com/ comme un bon aperçu / cheatsheet des regexps Vim.


1
Excellente réponse, en particulier avec pourquoi regex! = Regex. Quant à votre recherche et remplacement, c'est un peu délicat car le commentaire peut ou non avoir un :. Voir le fichier complet pour plus de détails github.com/squarefrog/dotfiles/blob/master/osx/…
squarefrog

8

Vous pouvez:

  • rechercher des lignes qui ne se terminent pas par #::/[^#]$/
  • remplacer #\s(.*)en début de ligne:s/\v^#\s(.*)/running "\1"/

Pour utiliser des groupes, vous devez soit:

  • échapper les crochets afin qu'ils deviennent une partie de la syntaxe regex:, \(.*\)ou
  • utilisez "magie" en commençant l'expression par \v:s/\v...

Le combiner:

:/[^#]$/s/\v^#\s(.*)/running "\1"/

1
Cela fonctionne très bien, merci d'avoir inclus une explication de la signification des balises plutôt que du texte que j'avais besoin d'écrire.
squarefrog

Merci d'avoir précisé que les crochets doivent être échappés afin de faire partie de la syntaxe regex. J'ai toujours eu un problème avec les groupes d'expression régulière pour les faire travailler.
Adama
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.