Amb el Terminal: Ús d'expressions regulars II: Reemplaçaments

en el meu article anterior us he explicat a un nivell bàsic com funcionen cadascun dels caràcters especials més usats de les expressions regulars. Amb aquestes expressions regulars és possible fer cerques complexes en fitxers de text o en la sortida d'altres ordres. En aquest article vaig a explicar com usar la comanda set per buscar i reemplaçar text d'una manera molt més potent que simplement canviar un text per un altre.

Una mica més sobre la comanda grep

Abans de començar a parlar sobre set, m'agradaria comentar alguna cosa més sobre la comanda grep per completar una mica el que s'ha explicat en l'anterior article. Tot el que vaig a dir serà rellevant per a aquest també. Més endavant veurem la relació que hi ha entre això i les recerques.

Combinant expressions regulars

Molts dels caràcters especials dels que us he parlat en l'article anterior es poden combinar, no només amb altres caràcters, sinó amb expressions regulars senceres. La forma de fer això és utilitzar parèntesis per formar una subexpressió. Anem a veure un exemple d'això. Comencem descarregant un text que ens serveixi per fer proves. Es tracta d'una llista de frases. Per això farem servir la següent comanda:

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

 Això us deixés en el directori on el lanzéis un fitxer amb nom «frases». Podeu obrir-lo per fer-li un cop d'ull i riure una mica. 🙂

Ara anem a suposar que volem trobar les frases que tinguin exactament 6 paraules. La dificultat està en formar una expressió regular que aparelli amb cada paraula. Una paraula és una seqüència de lletres ja siguin majúscules o minúscules, el que seria alguna cosa així com '[a-zA-Z]+', Però també cal especificar que aquestes lletres han d'estar separades per altres caràcters que no siguin lletres, o sigui que seria alguna cosa com '[a-zA-Z]+[^a-zA-Z]+'. Recordem: el «^» com a primer caràcter dins dels claudàtors indica que volem aparellar amb caràcters que no estan en els rangs i el «+» indica a 1 o més caràcters.

Ja tenim una expressió regular que pot aparellar amb una paraula. Per aparellar-la amb 6, caldrà repetir-la 6 cops. Per això fèiem servir les claus, però no serveix posar '[a-zA-Z]+[^a-zA-Z]+{6}', Perquè el 6 repetiria l'última part de l'expressió regular i el que volem és repetir-la tota, així que el que cal posar és això: '([a-zA-Z]+[^a-zA-Z]+){6}'. Amb els parèntesis formem una subexpressió i amb les claus la repetim 6 cops. Ja només falta afegir un «^» davant i un «$» darrere per aparellar amb la línia sencera. La comanda és el següent:

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

I el resultat és just el que volíem:

Està més cantat que la Macarena. Estàs més acabat que Luis Aguilé. Tens menys cultura que una pedra. Saps més idiomes que Cañita Brava. Té més arrugues que Tutan Khamón. Saps menys que Rambo de puericultura.

Fixeu-vos en que posem el paràmetre -I perquè volem fer servir expressions regulars esteses perquè funcioni el «+». Si féssim servir les bàsiques, caldria escapar els parèntesis i les claus.

Referències cap enrere o backreferences

Si teniu instal·lat algun corrector ortogràfic, probablement tindreu una llista de paraules en /usr/share/dict/words. Si no és així, podeu instal·lar-la en arch amb:

sudo pacman -S words

O en debian amb:

sudo aptitude install dictionaries-common

Si voleu podeu fer-li un cop d'ull a el fitxer per veure quines paraules té. En realitat és un enllaç a el fitxer de paraules de l'idioma en què estigui vostra distro. Es poden tenir diversos fitxers de paraules instal·lats alhora.

Anem a fer servir aquest fitxer. Resulta que tenim molta curiositat per saber tots els palíndroms de set lletres que hi ha. Pel qual no ho sàpiga: Un palíndrom és una paraula capicua, o sigui, que es pot llegir igual d'esquerra a dreta que de dreta a esquerra. Provem la següent comanda:

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

Té un aspecte una mica estrany, oi? Si ho provem, el resultat dependrà de l'idioma de la vostra distro i de les paraules que hi hagi en la vostra llista, però en el meu cas, amb l'idioma espanyol, el resultat és aquest:

anilina recocer rodador

Anem a veure com funciona aquesta expressió regular.

A part de l' «^» i el «$», que ja sabem per a què serveix, el primer que veiem a l'esquerra són tres grups de punts tancats entre parèntesis. Que no us confonguin les barres que hi ha davant de cada parèntesi. Són per escapar els parèntesis perquè estem fent servir expressions regulars bàsiques, però no tenen cap altre significat. L'important és que estem demanant amb els punts tres caràcters qualssevol, però cada un d'aquests punts estan tancats entre parèntesis. Això serveix perquè guardi els caràcters que encaixen amb aquests punts de manera que es pugui tornar a fer referència a ells des de l'expressió regular. Aquest és un altre ús dels parèntesis que serà molt útil més endavant per fer reemplaçaments.

Aquí és on vénen els tres nombres que hi ha a continuació amb la barra davant. En aquest cas, la barra sí que és important. Serveix per indicar que el nombre que hi ha a continuació és una backreference i està fent referència a un dels parèntesis anteriors. Per exemple: \ 1 fa referència a el primer parèntesi, \ 2 a la segona i així successivament.

O sigui que amb l'expressió regular que hem posat, el que busquem són totes les paraules que comencin per quatre lletres qualssevol i després tinguin una lletra que sigui igual que la tercera, una altra que sigui igual que la segona i una altra que sigui igual que la primera. El resultat són els palíndroms de set lletres que estiguin en la llista de paraules. Tal com volíem.

Si estiguéssim fent servir expressions regulars esteses, no caldria escapar els parèntesis, però amb expressions regulars esteses no funcionen les backreferences en tots els programes perquè no estan estandarditzades. No obstant això, amb grep funcionen, o sigui que aquesta pot ser una altra forma de fer el mateix. Podeu provar-ho si voleu.

Expressions de reemplaçament: La comanda set

A més de fer cerques, una de les millors utilitats de les expressions regulars és reemplaçar textos complexos. Per a això, una forma de fer-ho és amb la comanda set. La potència de la comanda set va molt més enllà de reemplaçar textos, però aquí vaig a utilitzar-lo per això. La sintaxi que vaig a usar amb aquesta comanda és la següent:

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

O també:

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

On regex serà l'expressió regular de recerca i REPL la de reemplaçament. Tingueu en compte que aquesta comanda no reemplaça realment res en el fitxer que li indiquem, sinó que el que fa és mostrar-nos el resultat de la substitució a la terminal, així que no us espanteu per les comandes que vaig a posar a continuació. Cap d'ells va modificar cap fitxer del vostre sistema.

Comencem amb un exemple senzill. Tots tenim en el directori / etc diversos fitxers de configuració que, normalment, tenen comentaris que comencen per «#». Suposem que volem veure un d'aquests fitxers sense els comentaris. Per exemple, vaig a fer-ho amb el fstab. Podeu provar amb el que vulgueu.

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

No vaig a posar aquí el resultat de la comanda perquè depèn del que tingueu en el vostre fstab, però si compareu la sortida de la comanda amb el contingut de l'arxiu veureu que tots els comentaris han desaparegut.

En aquesta comanda l'expressió de recerca és «#.*«, O sigui un« # »seguit de qualsevol nombre de caràcters, és a dir, els comentaris. I l'expressió de reemplaçament, si us fixeu en les dues barres seguides, veureu que no n'hi ha cap, així que el que està fent és substituir els comentaris per res, o sigui, esborrar-los. Més senzill impossible.

Ara farem el contrari. Suposem que el que volem és comentar totes les línies de l'arxiu. Provem així:

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

Veureu que, a la sortida de la comanda, totes les línies comencen per un coixinet i un espai en blanc. El que hem fet és substituir els inicis de línia per «# «. Aquest també és un exemple bastant simple en el qual el text pel qual es reemplaça és sempre el mateix, però ara anem a complicar-ho una mica més.

La gràcia dels reemplaçaments està en que en l'expressió de reemplaçament es poden utilitzar backreferences com les que us he explicat abans. Tornem a el fitxer de frases que ens hem descarregat a el principi de l'article. Anem a ficar entre parèntesis totes les lletres majúscules que hi hagi, però ho farem amb una ordre:

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

El que tenim aquí és una backreference en l'expressió de reemplaçament que fa referència a l'parèntesi que hi ha a l'expressió de cerca. Els parèntesis que hi ha en l'expressió de reemplaçament són parèntesi normals. En l'expressió de reemplaçament no tenen cap significat especial, es posen tal qual. El resultat és que totes les lletres majúscules es reemplacen per aquesta mateixa lletra, sigui el que sigui, amb parèntesis voltant.

Hi ha un altre caràcter que també es pot usar en l'expressió de reemplaçament, és «&» i es reemplaça per tot el text aparellat per l'expressió de cerca. Un exemple amb això podria ser ficar totes les frases de el fitxer entre cometes. Això es pot aconseguir amb aquesta comanda:

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

El funcionament d'aquesta comanda és molt similar a l'anterior, només que ara el que reemplacem és la línia sencera per aquesta mateixa línia amb cometes voltant. Com estem fent servir «&» no cal posar parèntesis.

Alguns comandaments útils amb expressions regulars

A continuació us deixaré uns quants comandaments que em semblen útils o curiosos i que utilitzen expressions regulars. Amb aquestes comandes es veu molt millor la utilitat de les expressions regulars que amb els exemples que us he posat fins ara, però em semblava important explicar una mica de el funcionament de les expressions regulars per poder entendre aquestes.

  • Mostrar les seccions d'una pàgina de l'manual:

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

Per descomptat, podeu canviar l'ordre bash pel que vulgueu. I després des man, podeu anar directament a la secció que us interessa usant, com no, una expressió regular. Premeu «/» per començar a buscar i escriviu «^ALIASES$»Per anar a la secció aliases, per exemple. Crec que aquest és el primer ús que vaig començar a fer de les expressions regulars fa ja uns quants anys. Moure per algunes pàgines de l'manual és gairebé impossible si no es fa servir algun truc com aquest.

  • Mostra els noms de tots els usuaris de la màquina inclosos els especials:

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

  • Mostra els noms dels usuaris, però només els que tenen shell:

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

Realment es pot fer amb una sola expressió regular, però la forma de fer-ho va més enllà del que us he explicat en aquests articles, així que ho he fet combinant dos ordres.

  • Inserir una coma davant de les tres últimes xifres de tots els números que hi hagi al fitxer numbers:

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

Només funciona amb números de fins a 6 dígits, però es pot llançar més d'una vegada per col·locar separadors en els altres grups de tres xifres.

  •  Extreure totes les adreces de correu electrònic d'un fitxer:

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

  • Separar el dia, mes i any de totes les dates que apareguin en un fitxer:

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

  • Descobrir la nostra IP local:

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

Això també es pot fer amb un sol comandament set, però millor ho va separar en un grep i un set per simplificar-lo.

Algunes adreces útils

Us deixo a continuació algunes adreces que poden ser útils relacionades amb les expressions regulars:

  • Regular expression library: Es tracta d'una biblioteca d'expressions regulars en la qual podeu buscar expressions regulars relacionades amb el tema que us interessi. Per a buscar adreces web, DNI o el que sigui.
  • RegExr: Un comprovador línia d'expressions regulars. Permet introduir un text i aplicar-li una expressió regular ja sigui de recerca o de reemplaçament. Dóna informació sobre l'expressió regular i té algunes opcions per canviar el seu comportament.
  • Regular Expressions Tester: És un addon per firefox que permet comprovar expressions regulars des del navegador.

Conclusió

Per ara això és tot. Les expressions regulars són complexes però útils. Porta temps aprendre-les, però si sou com jo, jugar amb elles us semblarà divertit i, a poc a poc anireu dominant-. És tot un món. Hi hauria molt a dir encara, sobre quantificadors lazy, expressions regulars estil PERL, multilínia, etc. I després cada programa té les seves característiques i les seves variants, així que el millor consell que us puc donar és mirar sempre la documentació de el programa que estigueu usant cada vegada que tingueu que escriure una expressió regular en un programa nou.

Ep! ... EH! ... DESPERTEU! ... ¿QUE FEU TOTS DORMINT? 🙂

Fonts

Algunes de les idees i exemples per a les expressions regulars d'aquest article les he pres d'aquí:


Deixa el teu comentari

La seva adreça de correu electrònic no es publicarà. Els camps obligatoris estan marcats amb *

*

*

  1. Responsable de les dades: Miguel Ángel Gatón
  2. Finalitat de les dades: Controlar l'SPAM, gestió de comentaris.
  3. Legitimació: El teu consentiment
  4. Comunicació de les dades: No es comunicaran les dades a tercers excepte per obligació legal.
  5. Emmagatzematge de les dades: Base de dades allotjada en Occentus Networks (UE)
  6. Drets: En qualsevol moment pots limitar, recuperar i esborrar la teva informació.

  1.   ILAV va dir

    Magistral !!!

    1.    hexborg va dir

      No és per a tant, però moltes gràcies. Espero que a la gent li agradi. 🙂

      1.    oscar va dir

        M'agrada ha!

        1.    hexborg va dir

          Llavors hauré fet alguna cosa bé. ¡Jajajaja !! 🙂

          Moltes gràcies pel teu comentari.

          1.    Blaire Pascal va dir

            Fotre segueix escrivint oncle, segueix així.

          2.    hexborg va dir

            @Blaire Pascal: Comentaris com el teu animen a fer-ho. 🙂 Moltes gràcies !!

      2.    Citux va dir

        A mi també em va agradar ... gràcies 🙂

        1.    hexborg va dir

          Gràcies a tu per fer comentaris. Espero escriure uns quants més. 🙂

  2.   marià va dir

    Els teus posts són fantàstics, s'aprèn molt, millor dit, s'aprèn a realitzar les tasques de manera elegant i eficient.

    Has pensat a recopilar tots els teus posts de shell script? Ordenadets en un pdf serien un gran manual.

    Ànims i moltes gràcies!

    1.    hexborg va dir

      Moltes gràcies !! No és mala idea. De moment només són dos, però més endavant pensaré en això. 🙂

  3.   Kiyov va dir

    molt bon article, 5+.

    1.    hexborg va dir

      Gràcies. M'alegro que t'agradi. 🙂

  4.   sebastian va dir

    Excel·lent! Necessito canviar l'expressió i no es com fer-ho:
    192.168.0.138/Server per 192.168.0.111/datos
    El problema rau en el símbol «/».
    Estic usant la comanda:
    find. -name «* .txt» -exec set -i 's / Text1 / Text2 / g' {} \;
    Que serveix per realitzar aquest tipus de tasques remisivamente, però no aconsegueixo ...
    Algú sap com ho he de fer?
    Abraçada!
    Seba

    1.    hexborg va dir

      El que cal fer és escapar el caràcter així:

      find. -name «* .txt» -exec set -i 's / \ / Server / \ / dades / g' {} \;

      També pots utilitzar un altre separador en set. No té perquè ser una barra. Sigueu permet usar qualsevol caràcter. Per exemple, això seria més clar:

      find. -name «* .txt» -exec set -i 's | / Server | / dades | g' {} \;

      I si vas a copiar i enganxar les ordres des d'aquest comentari ull amb les cometes, que wordpress les canvia per les tipogràfiques. 🙂

      Salutacions.

  5.   sebastian va dir

    Excel·lent !!!!
    Caminava buscant aquesta solució feia temps.
    Aquí deixo la comanda complet que he utilitzat

    find. -name «* .txt» -exec set -i 's | 192 \ .168 \ .0 \ 238 \ / Server | 192 \ .168 \ .0 \ .111 \ / dades | g' {} \;

    L'avantatge d'aquesta comanda és que canvia tots els fitxers .txt (o l'extensió que vulguis) recursivament ... Cal tenir molta cura!
    Però és molt útil !!!

    Bé, gràcies per tot i mil felicitacions a el grup sencer.
    Sempre els llegeixo des del correu!
    abraçades
    Seba