Suchen und Ersetzen mit regulären Ausdrücken

25

Ich habe eine Datei mit einer Reihe von Benutzervorgaben. Ich möchte einen Teil des Texts ändern, habe aber Probleme, einen Matcher und einen Ersatz zu finden. Mit dem folgenden Beispiel:

###############################################################################
# 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

Ich möchte ersetzen # Trackpad: ...mitrunning "Trackpad: ..."

Als ich das Problem aufschloss, kam ich mit einem Regex-Tester auf etwas:

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

Wenn ich dies in Vim versuche und verwende, funktioniert es bei mir nicht:

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

Ich denke, mein Problem besteht aus zwei spezifischen Fragen:

  1. Wie kann ich vermeiden, nach \nZeichen zu suchen , und stattdessen sicherstellen, dass #sie nicht am Ende der Suchgruppe angezeigt werden?
  2. Wie kann ich Erfassungsgruppen effektiv verwenden?

Im Folgenden finden Sie einige gute Antworten. Es ist schwierig, zwischen allen drei zu wählen, aber ich bin der Meinung, dass die gewählte Antwort für meine ursprüngliche Spezifikation am genauesten ist. Ich empfehle Ihnen, alle drei Antworten mit der aktuellen Datei zu versuchen, um zu sehen, wie Sie mit ihnen umgehen.

squarefrog
quelle

Antworten:

18

Nur um klar zu sein ... Ich glaube, Sie haben darum gebeten, dass dies das Ergebnis der Substitution ist?

###############################################################################
# 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

In diesem Fall empfehle ich den folgenden Befehl:

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

Erklärung des Musters

:s/PATTERN/REPLACEMENT/ist der Ersatzbefehl . Durch die prozentuale Anmeldung :%swird die gesamte Datei bearbeitet und nicht nur die aktuelle Zeile.

Der \n\nbesagt, dass die interessierende Zeile nach einer Leerzeile stehen muss. Wenn Ihnen die vorangegangene Leerzeile egal ^wäre, würde dies ausreichen.

#\s\+Entspricht einem Hash-Zeichen, gefolgt von einem oder mehreren Leerzeichen. \(.*\)erfasst den gesamten nachfolgenden Text in der Zeile.

Erläuterung des Ersetzungstextes

^M^Mfügt zwei Zeilenenden ein, um die \n\nim Muster vorhandenen zu ersetzen . Andernfalls würde der Text an das Ende der Zeile vor der Leerzeile verschoben. ^MDrücken Sie zum Eingeben jeweils Ctrl-V Ctrl-M.

Fügen Sie dann die Zeichenfolge ein running, gefolgt von dem, was in den Klammern in doppelten Anführungszeichen steht.

200_erfolg
quelle
Ich kann Ihre Antwort nicht bearbeiten, aber ich glaube, Sie haben gemeint :%s/\v\n\n#\s+(.*)/^M^Mrunning "\1"/(die "magische" Flagge hinzugefügt). Es ist wirklich schwierig, eine richtige Antwort für meine ursprüngliche Frage zu finden, aber ich bin der Meinung, dass diese Antwort meiner ursprünglich erwarteten Antwort am nächsten kommt. Es ist auch das einzige, das in der gesamten Datei funktioniert, ohne dass ein Bereich ausgewählt werden muss.
Squarefrog
1
IIRC können Sie verwenden, \ranstatt ^Mneue Zeilen zu erhalten?
muru
11

Ich würde etwas verwenden wie:

:s/^#\s\+\(.\{-}\):/running "\1":/
  • ^#den #am Zeilenanfang verankerten Buchstaben finden (dies beantwortet Frage 1)
  • \s\+ um ein oder mehrere Leerzeichen zuzuordnen
  • \( eine Gruppe gründen (dies beantwortet Frage 2)
  • .\{-}\ein beliebiges Zeichen 0-mal oder öfter auf nicht gierige Weise zu finden ; Dies unterscheidet sich darin, .*dass es versucht, so wenig wie möglich und nicht so viel wie möglich zusammenzubringen. Versuchen Sie, ein :Zeichen in den Kommentar einzufügen, um zu sehen, warum dies wichtig ist
  • \) um die Untergruppe zu beenden.
  • : Entspricht einem Literal :

Wir ersetzen dies dann durch den gewünschten Text und \1verweisen auf die Gruppe, die wir erfasst haben.

Ich habe etwas mit einem Regex-Tester gefunden

Die Syntax für reguläre Ausdrücke ähnelt der Wiki-Syntax: Es gibt einige davon, alle sehen auf einen Blick gleich aus, keiner von ihnen ist offensichtlich besser als jeder andere, aber es gibt viele Unterschiede.

Heutzutage sind die sogenannten "Perl-kompatiblen" regulären Ausdrücke in den meisten Sprachen de facto die Standardeinstellung, aber reguläre Vim-Ausdrücke sind nicht mit Perl-kompatiblen Ausdrücken kompatibel! Die Syntax von Vim regexp geht mindestens auf die siebziger Jahre zurück, als Perl noch nicht einmal da war.

Sie können dies an den Untergruppen erkennen, die Sie verwenden müssen \(und nicht ((dies ist mit der POSIX-Basissyntax kompatibel, jedoch nicht mit der allgemeineren POSIX-Extended-Syntax oder Perl-Syntax). Sie können dies steuern, indem Sie das \vFlag in einem Muster hinzufügen (siehe :help /\vDetails). Dadurch wird es "kompatibler", aber nicht vollständig (Sie müssen es beispielsweise immer noch .{-}für nicht gierige Matches verwenden).

Dies könnte erklären, warum die Verwendung eines "Regex-Testers" fast, aber nicht ganz funktioniert.

http://www.vimregex.com/ als gute Übersicht / Spickzettel von Vim RegExps.

Martin Tournoij
quelle
1
Tolle Antwort, besonders, wenn es darum geht, warum Regex! = Regex. Das Suchen und Ersetzen ist etwas knifflig, da der Kommentar möglicherweise einen enthält oder nicht :. Weitere Informationen finden Sie in der vollständigen Datei github.com/squarefrog/dotfiles/blob/master/osx/…
squarefrog
8

Sie können:

  • suche nach Zeilen, die nicht enden mit #::/[^#]$/
  • Ersetzen Sie #\s(.*)am Anfang der Zeile:s/\v^#\s(.*)/running "\1"/

Um Gruppen zu verwenden, müssen Sie entweder:

  • Lassen Sie die eckigen Klammern los, damit sie Teil der regulären Syntax werden:, \(.*\)oder
  • Verwenden Sie "Magie", indem Sie den Ausdruck beginnen mit \v:s/\v...

Kombinieren Sie es:

:/[^#]$/s/\v^#\s(.*)/running "\1"/
muru
quelle
1
Dies funktioniert hervorragend, dank der Tatsache, dass Sie eine Erklärung der Bedeutung der Tags und nicht nur des Texts, den ich schreiben musste, beigefügt haben.
Squarefrog
Vielen Dank, dass Sie klargestellt haben, dass die Klammern maskiert werden müssen, um Teil der Regex-Syntax zu werden. Ich hatte immer ein Problem mit Regex-Gruppen, wenn es darum ging, sie zum Arbeiten zu bringen.
Adama