Find og erstat ved hjælp af regulære udtryk

Jeg har en fil med en masse brugerindstillinger. Jeg vil ændre noget af teksten, men jeg kæmper kommer med en matcher og erstatning. Brug følgende eksempel:

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

Jeg vil gerne erstatte # Trackpad: ... med running "Trackpad: ..."

Ved at bryde problemet ned kom jeg på noget ved hjælp af en regex-tester:

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

Hvis jeg prøver at bruge dette i Vim, fungerer det ikke for mig:

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

Jeg antager, at mit problem koger ned til to specifikke spørgsmål:

  1. Hvordan kan jeg undgå at søge på \n tegn, og i stedet sørge for at # ikke vises i slutningen af søgegruppen?
  2. Hvordan kan jeg effektivt bruge fangstgrupper?

Der er nogle gode svar nedenfor. Svært at vælge mellem alle tre, men jeg føler, at det valgte svar er det mest nøjagtige til min originale specifikation. Jeg anbefaler, at du prøver alle tre svar med faktiske fil for at se, hvordan du synes om dem.

Svar

Bare for at være klar … Jeg tror, du bad om, at dette skulle være resultatet af udskiftningen?

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

I så fald anbefaler jeg følgende kommando:

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

Forklaring til mønsteret

:s/PATTERN/REPLACEMENT/ er erstatning -kommandoen. Procenttegnet i :%s får det til at fungere på hele filen i stedet for kun den aktuelle linje.

\n\n siger, at linjen af interesse skal forekomme efter en tom linje. Hvis du ikke var ligeglad med den foregående tomme linje, ville ^ være tilstrækkelig.

#\s\+ matcher en hash tegn efterfulgt af et eller flere tegn i mellemrummet. \(.*\) fanger al efterfølgende tekst på linjen.

Forklaring til erstatningsteksten

^M^M indsætter to ender af linjer for at erstatte \n\n, der var til stede i mønsteret. Ellers flyttes teksten til slutningen af linjen forud for den tomme linje. For at skrive hver ^M skal du trykke på Ctrl-V Ctrl-M .

Indsæt derefter strengen running efterfulgt af hvad der blev fanget i parentes inden for dobbelt anførselstegn.

Kommentarer

  • Jeg kan ikke redigere dit svar, men jeg tror, du mente :%s/\v\n\n#\s+(.*)/^M^Mrunning "\1"/ (tilføjede ” magi ” flag). Det er virkelig svært at vælge et korrekt svar på mit originale spørgsmål, men jeg føler, at dette svar er tættest på mit oprindelige forventede svar. Det er også den eneste, der fungerer gennem hele filen uden behov for at vælge et interval.
  • IIRC kan du bruge \r i stedet for ^M for at få nye linjer?

Svar

Jeg vil bruge noget som:

:s/^#\s\+\(.\{-}\):/running "\1":/ 
  • ^# for at matche # forankret i starten af linjen (dette besvarer spørgsmål 1)
  • \s\+ for at matche ethvert hvidt mellemrum en eller flere gange
  • \( for at starte en gruppe (dette besvarer spørgsmål 2)
  • .\{-}\ for at matche et hvilket som helst tegn 0 eller flere gange i en ikke-grådig måde ; dette er forskelligt fra .* ved, at det forsøger at matche så lidt som muligt og ikke så meget som muligt. Prøv at tilføje et : tegn i kommentaren for at se, hvorfor dette betyder noget
  • \) for at afslutte undergruppen.
  • : matcher en bogstavelig :

Vi erstatter derefter dette med teksten du ønsker, og brug \1 til at henvise til den gruppe, vi fangede.

Jeg kom på noget ved hjælp af en regex-tester

Syntaks til regulært udtryk ligner lidt wiki-syntaks: der er en flok af dem, de ser alle ens ud med et overblik, ingen af dem er tydeligvis bedre end nogen anden, men der er mange forskelle.

I dag er de såkaldte “Perl-kompatible” regulære udtryk de facto standard på de fleste sprog, men Vim regulære udtryk er ikke kompatibel med Perl-kompatible udtryk! Vim regexp-syntaks går tilbage til i det mindste “70”, da Perl ikke engang var omkring.

Du kan se dette med undergrupperne, hvor du skal bruge \( og ikke ( (dette er kompatibelt med POSIX “grundlæggende” syntaks, men ikke med den mere almindelige POSIX “udvidede” syntaks eller Perl-syntaks).Du kan kontrollere dette ved at tilføje \v flag i et mønster (Se :help /\v for detaljer), vil dette gøre det “mere kompatibelt”, men ikke fuldstændigt (du skal stadig bruge .{-} til f.eks. ikke-grådige kampe)

Så dette kan forklare, hvorfor brug af “en regex-tester” næsten, men ikke helt, fungerer.

http://www.vimregex.com/ som et godt overblik / cheatsheet over Vim regexps.

Kommentarer

Svar

Du kan:

  • søge efter linjer, der ikke slutter i #: :/[^#]$/
  • erstatt #\s(.*) i starten af linjen: s/\v^#\s(.*)/running "\1"/

For at bruge grupper skal du enten:

  • slippe for parenteserne, så de bliver en del af regex-syntaksen: \(.*\), eller
  • brug “magi” af begynder udtrykket med \v: s/\v...

Kombinerer det:

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

Kommentarer

  • Dette fungerer glimrende, tak for at have inkluderet en forklaring på, hvad tags betyder, snarere end blot den tekst, jeg havde brug for skriv.
  • Tak for at gøre det klart, at parenteserne har t o undslippe for at blive en del af regex-syntaksen. Jeg har altid haft et problem med regex-grupper for at få dem til at arbejde.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *