Med terminal: bruk av regulære uttrykk II: erstatninger

I meg selv forrige artikkel Jeg har fortalt deg på et grunnleggende nivå hvordan hver av de mest brukte spesialtegnene til vanlige uttrykk fungerer. Med disse regulære uttrykkene er det mulig å gjøre komplekse søk i tekstfiler eller i utdata fra andre kommandoer. I denne artikkelen skal jeg forklare hvordan du bruker kommandoen sed for å finne og erstatte tekst på en mye kraftigere måte enn å bare endre en tekst for en annen.

Litt mer om grep-kommandoen

Før jeg begynner å snakke om sed, vil jeg kommentere litt mer om grep-kommandoen for å fullføre det som ble forklart i forrige artikkel litt. Alt jeg skal si vil være relevant for denne også. Senere vil vi se forholdet mellom dette og søk.

Kombinere regulære uttrykk

Mange av spesialtegnene som jeg har snakket om i forrige artikkel kan kombineres, ikke bare med andre tegn, men med hele regulære uttrykk. Måten å gjøre dette på er å bruke parenteser til å danne et underuttrykk. La oss se et eksempel på dette. La oss starte med å laste ned en tekst som vi kan bruke til testing. Det er en liste over setninger. For det skal vi bruke følgende kommando:

curl http://artigoo.com/lista-de-frases-comparativas-comicas 2>/dev/null | sed -n 's/.*\(.*\.\)<\/p>/\1/gp' > frases

 Dette vil la deg være i katalogen der du starter en fil med navnet «fraser». Du kan åpne den for å se på den og le litt. 🙂

La oss anta at vi ønsker å finne setningene som har nøyaktig 6 ord. Vanskeligheten er å danne et regulært uttrykk som samsvarer med hvert ord. Et ord er en sekvens av bokstaver, enten store eller små bokstaver, som vil være omtrent som '[a-zA-Z]+', men du må også spesifisere at disse bokstavene må skilles med andre tegn enn bokstaver, det vil si at det vil være noe lignende '[a-zA-Z]+[^a-zA-Z]+'. La oss huske: "^" som det første tegnet i parentes indikerer at vi ønsker å matche med tegn som ikke er i områdene, og "+" indikerer 1 eller flere tegn.

Vi har allerede et vanlig uttrykk som kan matche et ord. For å parre den med 6, må den gjentas 6 ganger. Til det brukte vi nøklene, men de er ubrukelige '[a-zA-Z]+[^a-zA-Z]+{6}', fordi 6 vil gjenta den siste delen av det regulære uttrykket og det vi ønsker er å gjenta det hele, så det vi må sette er dette: '([a-zA-Z]+[^a-zA-Z]+){6}'. Med parentesene danner vi et underuttrykk, og med selene gjentar vi det 6 ganger. Nå trenger du bare å legge til en "^" foran og en "$" bak for å matche hele linjen. Kommandoen er som følger:

grep -E '^([a-zA-Z]+[^a-zA-Z]+){6}$' frases

Og resultatet er akkurat det vi ønsket:

Det er mer sunget enn Macarena. Du er mer ferdig enn Luis Aguilé. Du har mindre kultur enn en stein. Du kan flere språk enn Cañita Brava. Han har flere rynker enn Tutan Khamón. Du vet mindre enn Rambo om barnepass.

Legg merke til at vi setter parameteren -E fordi vi vil bruke utvidede regulære uttrykk for å få "+" til å fungere. Hvis vi brukte de grunnleggende, måtte vi unnslippe parentes og seler.

Bakreferanser eller tilbakehenvisninger

Hvis du har en stavekontroll installert, vil du sannsynligvis ha en liste med ord i /usr/share/dict/words. Hvis ikke, kan du installere den i bue med:

sudo pacman -S words

Eller i debian med:

sudo aptitude install dictionaries-common

Hvis du vil, kan du se på filen for å se hvilke ord den har. Det er faktisk en lenke til ordfilen for språket distro er på. Du kan ha flere ordfiler installert samtidig.

Vi skal bruke den filen. Det viser seg at vi er veldig nysgjerrige på å kjenne alle palindromene med syv bokstaver der ute. For de som ikke vet: Et palindrom er et capicúa-ord, det vil si at det kan leses fra venstre til høyre så vel som fra høyre til venstre. La oss prøve følgende kommando:

grep '^\(.\)\(.\)\(.\).\3\2\1$' /usr/share/dict/words

Det ser litt rart ut, ikke sant? Hvis vi prøver det, vil resultatet avhenge av språket i distro og ordene i listen din, men i mitt tilfelle, med det spanske språket, er resultatet dette:

anilin anilin rullende

La oss se hvordan dette regulære uttrykket fungerer.

Bortsett fra "^" og "$", som vi allerede vet hva det er til, er det første vi ser til venstre tre grupper av punkter som er lukket i parentes. Ikke bli forvirret av stolpene foran hver parentes. De skal unnslippe parentesene fordi vi bruker grunnleggende regulære uttrykk, men de har ingen annen betydning. Det viktige er at vi ber om tre tegn med prikkene, men hver av disse punktene er omgitt av parenteser. Dette er for å lagre tegnene som samsvarer med disse punktene, slik at de kan henvises til igjen fra det vanlige uttrykket. Dette er en annen bruk av parenteser som vil komme til nytte senere når du skifter ut.

Det er her de tre tallene nedenfor kommer med skråstrek foran seg. I dette tilfellet er stangen viktig. Den brukes til å indikere at nummeret nedenfor er en tilbakeblikk og refererer til en av de forrige parentesene. For eksempel: \ 1 refererer til den første parentesen, \ 2 til den andre, og så videre.

Det vil si at med det vanlige uttrykket vi har satt, er det vi leter etter, alle ordene som begynner med fire bokstaver og deretter har en bokstav som er den samme som den tredje, en annen som er den samme som den andre og en annen som er den samme som den først. Resultatet er de syv bokstavpalindromene som er i ordlisten. Akkurat som vi ønsket.

Hvis vi brukte utvidede regulære uttrykk, trenger ikke parentesene å slippes unna, men med utvidede regulære uttrykk fungerer ikke referanser i alle programmer fordi de ikke er standardiserte. Imidlertid fungerer de med grep, så det kan være en annen måte å gjøre det samme på. Du kan prøve det hvis du vil.

Erstatningsuttrykk: sed-kommandoen

I tillegg til å søke, er en av de beste bruken av regulære uttrykk å erstatte komplekse tekster. For å gjøre dette, er en måte å gjøre det på med sed-kommandoen. Kraften til kommandoen sed går langt utover å erstatte tekst, men her skal jeg bruke den til det. Syntaksen som jeg skal bruke med denne kommandoen er følgende:

sed [-r] 's/REGEX/REPL/g' FICHERO

Eller også:

COMANDO | sed [-r] 's/REGEX/REPL/g'

Der REGEX vil være søke regulært uttrykk og REPL er erstatning. Husk at denne kommandoen egentlig ikke erstatter noe i filen vi indikerer, men hva den gjør er å vise oss resultatet av erstatningen i terminalen, så vær ikke redd av kommandoene jeg skal legge videre. Ingen av dem kommer til å endre noen filer på systemet ditt.

La oss starte med et enkelt eksempel. Vi har alle forskjellige konfigurasjonsfiler i / etc-katalogen som vanligvis har kommentarer som begynner med "#". Anta at vi vil se en av disse filene uten kommentarene. For eksempel skal jeg gjøre det med fstab. Du kan prøve med den du vil ha.

sed 's/#.*//g' /etc/fstab

Jeg kommer ikke til å legge resultatet av kommandoen her fordi det avhenger av hva du har i fstab, men hvis du sammenligner utdataene fra kommandoen med innholdet i filen, vil du se at alle kommentarene har forsvunnet.

I denne kommandoen er søkeuttrykket «#.*", Det er et" # "etterfulgt av et hvilket som helst antall tegn, det vil si kommentarene. Og erstatningsuttrykket, hvis du ser på de to stolpene på rad, vil du se at det ikke er noen, så det den gjør er å erstatte kommentarene med ingenting, det vil si å slette dem. Enklere umulig.

Nå skal vi gjøre det motsatte. Anta at det vi ønsker er å kommentere alle linjene i filen. La oss prøve slik:

sed 's/^/# /g' /etc/fstab

Du vil se at i linjens utgang begynner alle linjene med et hash-merke og et tomt mellomrom. Det vi har gjort er å erstatte begynnelsen på linjen med «# «. Dette er også et ganske enkelt eksempel der teksten som skal erstattes alltid er den samme, men nå skal vi komplisere det litt mer.

Erstatningens nåde er at du i erstatningsuttrykket kan bruke tilbakehenvisninger som de jeg fortalte deg før. La oss gå tilbake til setningsfilen som vi lastet ned i begynnelsen av artikkelen. Vi kommer til å sette i parentes alle store bokstaver som finnes, men vi vil gjøre det med en kommando:

sed 's/\([A-Z]\)/(\1)/g' frases

Det vi har her er en tilbakehenvisning i erstatningsuttrykket som refererer til parentesene i søkeuttrykket. Parentesene i erstatningsuttrykket er normale parenteser. I erstatningsuttrykket har de ingen spesiell betydning, de blir satt som de er. Resultatet er at alle store bokstaver blir erstattet av den samme bokstaven, uansett hva den er, med parenteser rundt den.

Det er et annet tegn som også kan brukes i erstatningsuttrykket, det er "&" og det erstattes av all teksten som matches av søkeuttrykket. Et eksempel på dette kan være å sette alle setningene i filen i anførselstegn. Dette kan oppnås med denne kommandoen:

sed 's/.*/"&"/g' frases

Driften av denne kommandoen er veldig lik den forrige, bare nå erstatter vi hele linjen med samme linje med anførselstegn rundt. Siden vi bruker "&", er det ikke nødvendig å sette parenteser.

Noen nyttige kommandoer med vanlige uttrykk

Her er noen få kommandoer som jeg synes er nyttige eller nysgjerrige, og som bruker vanlige uttrykk. Med disse kommandoene er bruken av regulære uttrykk mye bedre enn med eksemplene jeg har gitt deg så langt, men det virket viktig for meg å forklare noe om hvordan vanlige uttrykk fungerer for å forstå dem.

  • Vis deler av en manside:

man bash | grep '^[A-Z][A-Z ]*$'

Selvfølgelig kan du endre bash-kommandoen til hva du vil. Og så fra mann, kan du gå direkte til seksjonen som interesserer deg ved å bruke, selvfølgelig, et vanlig uttrykk. Du trykker på «/» for å begynne å søke og skrive «^ALIASES$»For eksempel å gå til ALIASES-delen. Jeg tror dette er den første bruken jeg begynte å gjøre av vanlige uttrykk for noen år siden. Å bevege seg gjennom noen sider i håndboken er nesten umulig uten et triks som dette.

  • Vis navnene på alle brukere av maskinen, inkludert spesielle:

sed 's/\([^:]*\).*/\1/' /etc/passwd

  • Vis brukernavn, men bare de med skall:

grep -vE '(/false|/nologin)$' /etc/passwd | sed 's/\([^:]*\).*/\1/g'

Det kan virkelig gjøres med et enkelt regulært uttrykk, men måten å gjøre det på går utover det jeg har fortalt deg i disse artiklene, så jeg har gjort det ved å kombinere to kommandoer.

  • Sett inn et komma før de tre siste sifrene av alle tallene i tallfilen:

sed 's/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g' numbers

Det fungerer bare med tall opptil 6 sifre, men det kan kalles mer enn en gang for å plassere skilletegn i de andre gruppene på tre sifre.

  •  Pakk ut alle e-postadressene fra en fil:

grep -E '\<[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\>' FICHERO

  • Skill dag, måned og år fra alle datoene som vises i en fil:

sed -r 's/([0-9]{2})[/-]([0-9]{2})[/-]([0-9]{4})/Día: \1, Mes: \2, Año: \3/g' FICHERO

  • Finn ut vår lokale IP:

/sbin/ifconfig | grep 'inet .*broadcast' | sed -r 's/[^0-9]*(([0-9]+\.){3}[0-9]+).*/\1/g'

Dette kan også gjøres med en enkelt sed-kommando, men jeg skiller den bedre inn i en grep og en sed for enkelhets skyld.

Noen nyttige adresser

Her er noen adresser som kan være nyttige i forbindelse med vanlige uttrykk:

  • Vanlig uttrykksbibliotek: Dette er et vanlig uttrykksbibliotek der du kan søke etter regulære uttrykk relatert til emnet som interesserer deg. For å søke etter nettadresser, ID eller hva som helst.
  • RegExr: En online sjekker for vanlig uttrykk. Den lar deg legge inn en tekst og bruke et vanlig uttrykk på den, enten søk eller erstatt. Det gir informasjon om det regulære uttrykket, og du har noen muligheter til å endre oppførselen.
  • Tester for vanlige uttrykk: Det er et tillegg for Firefox som lar deg sjekke vanlige uttrykk fra nettleseren.

Konklusjon

For nå er det alt. Regulære uttrykk er komplekse, men nyttige. Det tar tid å lære dem, men hvis du er som meg, vil det være morsomt å leke med dem, og litt etter litt vil du mestre dem. Det er en hel verden. Det ville være mye å si ennå, om late kvantifiseringsmidler, PERL-stil regex, multiline, etc. Og så har hvert program sine egenskaper og varianter, så det beste rådet jeg kan gi deg er å alltid se på dokumentasjonen til programmet du bruker hver gang du må skrive et vanlig uttrykk i et nytt program.

Hei! …HEI! … VÅKN OPP! ... HVA GJØR dere alle sovende? 🙂

Kilder

Noen av ideene og eksemplene for vanlige uttrykk i denne artikkelen har jeg tatt herfra:


Legg igjen kommentaren

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *

*

*

  1. Ansvarlig for dataene: Miguel Ángel Gatón
  2. Formålet med dataene: Kontroller SPAM, kommentaradministrasjon.
  3. Legitimering: Ditt samtykke
  4. Kommunikasjon av dataene: Dataene vil ikke bli kommunisert til tredjeparter bortsett fra ved juridisk forpliktelse.
  5. Datalagring: Database vert for Occentus Networks (EU)
  6. Rettigheter: Når som helst kan du begrense, gjenopprette og slette informasjonen din.

  1.   livlig sa

    Mesterlig !!!

    1.    hexborg sa

      Det er ikke så ille, men tusen takk. Håper folk liker det. 🙂

      1.    Oscar sa

        Jeg liker det ha!

        1.    hexborg sa

          Da må jeg ha gjort noe riktig. LOL !! 🙂

          Tusen takk for kommentaren din.

          1.    Blaire pascal sa

            Faen fortsett å skrive mann, fortsett det.

          2.    hexborg sa

            @Blaire Pascal: Kommentarer som din oppmuntrer det. Tusen takk !!

      2.    By sa

        Jeg likte det også ... takk 🙂

        1.    hexborg sa

          Takk for kommentaren. Jeg håper å skrive noen flere. 🙂

  2.   Marian sa

    Innleggene dine er fantastiske, du lærer mye, heller lærer du å utføre oppgaver på en elegant og effektiv måte.

    Har du tenkt på å samle alle shell-skriptinnleggene dine? Sortert i en pdf vil gjøre en flott manual.

    Skål og tusen takk!

    1.    hexborg sa

      Tusen takk!! Det er ikke en dårlig idé. For øyeblikket er det bare to, men jeg vil tenke på det senere. 🙂

  3.   Kiyov sa

    veldig god artikkel, 5+.

    1.    hexborg sa

      Takk skal du ha. Jeg er glad du liker det. 🙂

  4.   sebastian sa

    Utmerket! Jeg må endre følgende uttrykk, og jeg vet ikke hvordan jeg gjør det:
    192.168.0.138/Server av 192.168.0.111/data
    Problemet ligger i "/" symbolet.
    Jeg bruker kommandoen:
    finne. -navn "* .txt" -exec sed -i 's / TEXT1 / TEXT2 / g' {} \;
    Hva brukes til å utføre denne typen oppgaver remissivt, men jeg kan ikke ...
    Er det noen som vet hvordan jeg skal gjøre det?
    Klem!
    Seba

    1.    hexborg sa

      Det du må gjøre er å unnslippe karakteren slik:

      finne. -navn "* .txt" -exec sed -i 's / \ / Server / \ / data / g' {} \;

      Du kan også bruke en annen separator i sed. Det trenger ikke å være en bar. Sed lar enhver karakter brukes. For eksempel vil dette være tydeligere:

      finne. -navn "* .txt" -exec sed -i 's | / Server | / data | g' {} \;

      Og hvis du skal kopiere og lime inn kommandoene fra denne kommentaren, vær forsiktig med anførselstegnene, at wordpress endrer dem for de typografiske. 🙂

      Hilsener.

  5.   sebastian sa

    Utmerket!!!!
    Jeg har lett etter denne løsningen i lang tid.
    Her legger jeg igjen den komplette kommandoen jeg har brukt

    finne. -navn "* .txt" -exec sed -i s | 192 \ .168 \ .0 \ .238 \ / Server | 192 \ .168 \ .0 \ .111 \ / data | g '{} \;

    Fordelen med denne kommandoen er at den endrer alle .txt-filene (eller utvidelsen du vil ha) rekursivt ... Du må være veldig forsiktig!
    Men det er veldig nyttig !!!

    Vel, takk for alt og tusen gratulasjoner til hele gruppen.
    Jeg har alltid lest dem fra posten!
    Klemmer
    Seba