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… ;)

Veramente la notizia del secolo... come ho fatto a vivere fin'ora senza questo operatore?
:O :O :O :O
Commento di AleXit — 11 January 2008 @ 12:30
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
Commento di Treviño — 11 January 2008 @ 15:31
ottima segnalazione!!!
ciaociao
Commento di Fede — 11 January 2008 @ 21:27
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
Commento di farthest — 12 January 2008 @ 17:16
questa è la mia risposta http://paste.ubuntu-nl.org/51679/
Che serve questo comando?
exiv2 -T "$i";
Commento di farthest — 12 January 2008 @ 18:37
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.
Commento di Treviño — 12 January 2008 @ 20:41
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 :)
Commento di farthest — 13 January 2008 @ 15:06
La correzione è http://paste.ubuntu-nl.org/51773/
Commento di Treviño — 13 January 2008 @ 16:04