Zoek en vervang met reguliere expressies

Ik heb een bestand met een aantal standaard gebruikersinstellingen. Ik wil een deel van de tekst wijzigen, maar ik heb het moeilijk een matcher en vervanger bedenken. Met behulp van het volgende voorbeeld:

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

Ik “wil # Trackpad: ... vervangen with running "Trackpad: ..."

Toen ik het probleem oploste, bedacht ik iets met een regex-tester:

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

Als ik dit probeer te gebruiken in Vim, werkt het niet voor mij:

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

Ik denk dat mijn probleem neerkomt op twee specifieke vragen:

  1. Hoe kan ik voorkomen dat ik moet zoeken naar \n -tekens en in plaats daarvan ervoor zorgen dat # niet wordt weergegeven aan het einde van de zoekgroep?
  2. Hoe kan ik capture-groepen effectief gebruiken?

Hieronder staan een aantal geweldige antwoorden. Moeilijk om tussen alle drie te kiezen, maar ik denk dat het gekozen antwoord het meest nauwkeurig is voor mijn oorspronkelijke specificatie. Ik raad je aan om alle drie de antwoorden te proberen met het actuele bestand om te zien hoe je erover denkt.

Antwoord

Voor de duidelijkheid … ik denk dat je hebt gevraagd om dit het resultaat van de vervanging te zijn?

############################################################################### # 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 dat geval raad ik het volgende commando aan:

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

Uitleg van het patroon

:s/PATTERN/REPLACEMENT/ is het vervangende commando. Het procentteken in :%s zorgt ervoor dat het voor het hele bestand werkt, in plaats van alleen voor de huidige regel.

Het \n\n zegt dat de regel van belang na een lege regel moet komen. Als de voorafgaande lege regel je niets kon schelen, dan zou ^ volstaan.

#\s\+ komt overeen met een hash teken gevolgd door een of meer spaties. \(.*\) vangt alle volgende tekst op de regel op.

Uitleg van de vervangende tekst

^M^M voegt twee uiteinden van regels in om de \n\n te vervangen die in het patroon aanwezig waren. Anders zou de tekst naar het einde van de regel worden verplaatst voorafgaand aan de lege regel. Om elke ^M te typen, drukt u op Ctrl-V Ctrl-M .

Voeg vervolgens de tekenreeks running in, gevolgd door wat er tussen haakjes tussen dubbele aanhalingstekens is vastgelegd.

Opmerkingen

  • Ik kan je antwoord niet bewerken, maar ik denk dat je :%s/\v\n\n#\s+(.*)/^M^Mrunning "\1"/ bedoelde (voegde de ” magie toe ” vlag). Het is echt moeilijk te kiezen een correct antwoord op mijn oorspronkelijke vraag, maar ik denk dat dit antwoord het dichtst bij mijn oorspronkelijke verwachte antwoord komt. Het is ook de enige die in het hele bestand werkt zonder dat u een bereik hoeft te selecteren.
  • IIRC u kunt \r gebruiken in plaats van ^M om nieuwe regels te krijgen?

Antwoord

Ik zou zoiets als:

:s/^#\s\+\(.\{-}\):/running "\1":/ 
  • ^# om overeen te komen met het # teken verankerd aan het begin van de regel (dit beantwoordt vraag 1)
  • \s\+ om een of meerdere keren overeen te komen met een witruimte
  • \( om een groep te starten (hiermee wordt vraag 2 beantwoord)
  • .\{-}\ om een willekeurig teken 0 of meer keer in een niet-hebzuchtige manier ; dit verschilt van .* in die zin dat het zo min mogelijk probeert te matchen, en niet zo veel mogelijk. Probeer een : -teken toe te voegen aan de opmerking om te zien waarom dit ertoe doet.
  • \) om de subgroep te beëindigen.
  • : komt overeen met een letterlijke :

We vervangen dit dan door de tekst je wilt, en gebruik \1 om te verwijzen naar de groep die we hebben vastgelegd.

Ik heb iets bedacht met een regex-tester

De syntaxis van reguliere expressies lijkt een beetje op de wiki-syntaxis: er zijn er een aantal, ze zien er allemaal hetzelfde uit, geen van hen is duidelijk beter dan alle andere, maar er zijn veel verschillen.

Tegenwoordig zijn de zogenaamde “Perl-compatibele” reguliere expressies de de facto standaard in de meeste talen, maar Vim reguliere expressies zijn niet compatibel met Perl-compatibele uitdrukkingen! Vim regexp syntaxis gaat terug naar tenminste de “70” s, toen Perl niet eens in de buurt was.

Je kunt dit zien bij de subgroepen, waar je \( en niet ( (dit is compatibel met POSIX “basic” syntaxis, maar niet met de meer algemene POSIX “extended” syntaxis of Perl-syntaxis).U kunt dit regelen door de vlag \v in een patroon toe te voegen (zie :help /\v voor details), dit zal het “meer compatibel” maken, maar niet volledig (je moet nog steeds .{-} gebruiken voor bijvoorbeeld niet-hebzuchtige matches)

Dus dit zou kunnen verklaren waarom het gebruik van “a regex tester” bijna, maar niet helemaal, werkt.

http://www.vimregex.com/ als een goed overzicht / cheatsheet van Vim-regexps.

Reacties

Answer

Je kunt:

  • zoeken naar regels die niet eindigen in #: :/[^#]$/
  • vervang #\s(.*) aan het begin van de regel: s/\v^#\s(.*)/running "\1"/

Om groepen te gebruiken, moet je ofwel:

  • ontsnappen aan de haakjes zodat ze worden onderdeel van de regex-syntaxis: \(.*\), of
  • gebruiken “magic” door de uitdrukking beginnen met \v: s/\v...

Combineren:

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

Reacties

  • Dit werkt uitstekend, bedankt voor het toevoegen van een uitleg van wat de tags betekenen in plaats van alleen de tekst die ik nodig had schrijven.
  • Bedankt dat je duidelijk hebt gemaakt dat de haakjes t hebben o worden ontsnapt om onderdeel te worden van de regex-syntaxis. Ik had altijd een probleem met regex-groepen om ze aan het werk te krijgen.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *