That’s my Tumblog... Thoughts and Actions time by time!

Regexp in Bash con l’operatore =~

Nonostante sia potentissima, Bash – la shell più usata in ambienti UNIX – fino alla versione 3.0 (esclusa – changelog), non permetteva nativamente l’utilizzo di Espressioni Regolari, si poteva in qualche modo ovviare usando grep, sed, awk o expr.

Fortunatamente con la versione 3.0 è stato aggiunto il supporto nativo all’operatore =~ [fonte], che proviene dal Perl e permette un uso molto potente delle regexp, utilizzando solo le forze della nostra $SHELL.
Un esempio:

if [[ "Stringa" =~ S([a-z]+)a ]]; then
echo $BASH_REMATCH; # stampa "Stringa"
echo ${BASH_REMATCH[1]}; # stampa "tring"
fi

Capito? :)
Come potete vedere, il funzionamento è molto simile anche alla funzione preg_match in PHP, oltre allo stesso operatore del Perl: praticamente si confronta una stringa con un'espressione regolare (che deve essere indicata senza alcun apice ai bordi!), quindi se l'espressione regolare matcha la stringa, il testo corrispondente finisce nella variabile $BASH_REMATCH; qualora nell’espressione regolare si siano definite delle sottoespressioni (espressioni tra parentesi tonde, in questo caso: ([a-z]+)), allora il loro contenuto finisce in $BASH_REMATCH[$i] (con $i = 0 si intende $BASH_REMATCH stesso, mentre con $i = 1 la prima sottoespressione).

Siccome, a partire da Bash 3.2 l’espressione regolare non può essere quotata (ossia tra apici ' o "), qual’ora si analizzino stringhe con degli spazi bisogna usare l’escape . Ossia:

if [[ "Stringa bella Lunga con tanti Spazi" =~ S([a-z]+)a be.* L([a-z]*)a ]]; then
echo $BASH_REMATCH; # stampa "Stringa bella Lunga"
echo ${BASH_REMATCH[1]}; # stampa "tring"
echo ${BASH_REMATCH[2]}; # stampa "ung"
fi

Sottolineo che di default l’operatore binario è case sensitive, è tuttavia possibile usare rispettivamente shopt -s nocaseglob e shopt -u nocaseglob per attivare o disattivare il case insensitive (altre info).

Un esempio di utilizzo più pratico, nel mio caso, è stato quello di gestire alcune foto scattate col cellulare (un Motorola) leggerendo l’ora e la data di scatto dal nome del file (in formato DD-MM-YY_HHMM) e quindi salvando il timestamp nel file stesso, sia come parametro EXIF che come data di modifica.
Grazie a =~ e ad exiv2, tutto questo è stato possibile in 16 righe di script che in qualche secondo hanno sistemato oltre 650 foto… ;)

8 Commenti

  1. Oddio ma è fantastico!
    Veramente la notizia del secolo... come ho fatto a vivere fin'ora senza questo operatore?

    :O :O :O :O
  2. Stolto... :P
    Te non ti rendi conto di quanto può essere comodo un aggeggino del genere... Tempo fa mi sono messo a fare il parsing di uno XML in bash, ed usando solo sed, grep ed expr c'è voluto una vita :P
  3. non vedo l'ora di "doverlo"/"poterlo" usare e risparmiare un pò di righe :)

    ottima segnalazione!!!

    ciaociao
  4. Cavolo è proprio una bella utilità.

    Trevi è possibile fare una sottoespressione in una sottoespressione? e quale sarebbe il numero dell'array?

    in passato anch'io ho usato exiv2 per il timestamp ma non ero riuscito a capire qual'era la chiave per settare il timestamp :-(

    Ciao
  5. Ciao
    questa è la mia risposta http://paste.ubuntu-nl.org/51679/

    Che serve questo comando?


    exiv2 -T "$i";

  6. Sottoespressioini di sottoespressioni credo proprio che non si possano fare visto che la bash non supporta array multi-dimensionali, ma magari qualche hack c'è...!

    Cmq la tua risposta è sensata per un problema del genere, visto che bene o male le stringhe sono formattate a lunghezza fissa, però era per mostrare come funziona un po' =~ ;)

    Ad ogni modo quel comando, setta il timestamp del file chiamato alla data impostata nei parametri Exif.
  7. Trevi hai fatto benissimo a mostrare come funziona =~, non lo conoscevo. ;)

    ma: exiv2 -T "$i"; dove prende la variabile $i, non mi sembra dichiarata. :-?

    da quando hai detto sopra dovrebbe essere: exiv2 -T "$foto";
    o mi sbaglio??

    Ciao :)
  8. Esatto... Il fatto è che avevo scritto inizialmente il ciclo con il classico (almeno per me) for i in... Poi però per migliorarne la leggibilità ho sostituito $i con $foto, ma - come temevo - non ovunque :D.
    La correzione è http://paste.ubuntu-nl.org/51773/

Lascia un commento

TexTile: È possibile inserire commenti usando la sintassi Textile
Incollare contenuti: Per non creare problemi con il layout del sito e per non riempire la zona dedicata ai commenti con eccessivo materiale, vi prego di incollare i contenuti di file di log, script e simili inserendo qui il link che otterrete inserendo i vosti materiali sul servizio Ubuntu Pastebin (o alternativi)