| |||
| © Perl Mongers Italia. Tutti i diritti riservati. | |||
Il miglior modo per imparare a risolvere i problemi è crearseli... e quando non si riesce a crearli abbastanza buoni, conviene rubare quelli degli altri e tentare di risolverli per cercare di imparare qualcosa in più. Mi è accaduto proprio questo leggendo un post su una delle mailing-list che spulcio abitualmente. Il problema era il seguente: Voi penserete: "Beh? Apro il file con il mio editor preferito, cerco la parola che devo sostituire, e il gioco è fatto. Al massimo posso avventurarmi in qualche pipe tra sed e cat per automatizzare il processo". Ok! Aspettate un po' e vedremo se continuerete ad esserne così convinti :-D Immaginate di avere un file chiamato pippo.conf con molte righe tra le quali queste tre: (1) Load "radeon" . . . (N-1) Driver "radeon" (N) Driver "vesa" e immaginate ora di voler sostituire la parola "radeon" con "vesa", ma di volerlo fare solamente nella riga N-1 e non da altre parti. Qualcuno potrebbe obiettare: "Apri il file con un editor qualsiasi ed effettua la sostituzione una volta per tutte!". Ma il nostro amico in mailing-list aveva bisogno di ripetere questa operazione più e più volte su file diversi. Come automatizzare il tutto?L'utente della mailing list aveva pensato di utilizzare degli strumenti evergreen come 'sed', 'grep' e 'awk': alla peggio una combinazione dei tre tramite pipe; noi che vogliamo fare sempre tutto con Perl abbiamo risposto: "Perché non usi Perl?" Ma cerchiamo di capire la strada che intendeva percorrere: Utilizziamo sedsed -e 's/radeon/vesa/' pippo.conf > pippo.conf Così facendo vengono sostituite entrambe le occorrenze di radeon all'interno del file. Ma non era esattamente quello che volevamo, quindi non ci basta: potremmo pensare di far interagire sed con grep in modo da scegliere la riga da cambiare? "Pipe tra cat, grep e sed più redirezione"cat pippo.conf | grep Driver | sed 's/radeon/vesa/' > pippo.conf Questa serie di pipe anche se ben architettate non assolvono il compito che ci eravamo prefissati:
Altra soluziona ingegnosa e molto laboriosa: sostituiamo tutte le tabulazioni con _ o altro carattere a piacerecat pippo.conf | tr '\t' '_' > pippo.conf e ora è facile cambiare quello che ci interessa cat pippo.conf | tr '\t' '_' | sed s/_Driver__\"radeon\"/_Driver__\"vesa\"/g > pippo.conf poi rimettiamo a posto le tabulazioni e salviamo in un altro file con cat /etc/X11/xorg.conf | tr '\t' '_' | sed s/_Driver__\"radeon\"/_Driver__\"vesa\"/g | tr '_' '\t' > prova Il nostro obiettivo era sostituire una parola, ma se per farlo abbiamo bisogno di tre comandi... stiamo freschi! E i comandi diventano quattro se salviamo da una parte il file per non rischiar di far casini! Ammettendo che tutto ciò funzioni, (abbiamo supposto che ci sia il carattere di tabulazione a separare le parole e non semplici spazi), ci accorgiamo subito che questa soluzione è anch'essa troppo laboriosa. "Smettila, e dimmi come lo faresti in Perl!" Una soluzione Perlistica"Ok, ci sto, vediamo se ti piace!": perl -p -i.bak -e 's/radeon/vesa/ if m/Driver/' pippo.conf questo comando assolve pienamente il compito che ci eravamo prefissati: sostituisce radeon con vesa solamente nella riga in cui è presente anche la parola Driver e in più crea un file di backup chiamato pippo.conf.bak. Per giunta tutto in un colpo solo! Analiziamolo in tutte le sue parti.
Ma come lo fa?Abbiamo detto che con -p, Perl cicla linea per linea e mette a disposizione il contenuto di ciascuna di esse in una variabile particolare: $_. Questa variabile, come si può notare dalla sua non presenza, è implicita. Sì! Ed è proprio la stessa variabile sulla quale andiamo a compiere l'operazione di sostituzione e confronto: s/radeon/vesa/ if m/Driver/ questa riga vuol dire: prendi il contenuto della variabile $_ e sostituisci la prima occorrenza di radeon con vesa se in $_ c'è la sequenza 'Driver', potevamo quindi scrivere: $_ =~ s/radeon/vesa/ if $_ =~ m/Driver/ Le due scritture sono perfettamente equivalenti ma quella senza la variabile $_ rispetta maggiormente uno degli obiettivi progettuali di Perl: essere assimilabile ad un linguaggio parlato. Questa modalità di sostituzione è molto elegante, compatta e di facilissima lettura, basta farci l'occhio. Abbiamo scoperto un'altra peculiarità di Perl, il cui motto è TMTOWTDI: "c'è più di un modo per fare la stessa cosa". E quello strano =~ cos'è?È un operatore di binding, praticamente lega il pattern di ricerca che si trova a destra con l'argomento a sinistra. Lega?Sì! A seconda di ciò che c'è a destra va a:
nella $stringa alla sua sinistra. ConclusioniCome sempre, in questo articoli tentiamo di mettere la pulce nell'orecchio del curiosone che è in ognuno di noi. Perl è considerato il "Coltellino Svizzero" del nostro sistema: abbiamo usato solo una delle sue mille lame, a voi il compito (spero il piacere) di scoprire le altre!
| |||