Un elefante scriveva uno script in bash…

konsole_48Che il valzer dei luoghi comuni su Linux abbia inizio: “Linux è per programmatori”, “È tutto a linea di comando”, “È difficile da usare”, “Bisogna usare il terminale!”.

Ok, finita la solita sequela di affermazioni trite, ritrite e smentite, passiamo a scrivere un piccolo programma da eseguire in un terminale. Ehm😳 dicevo… per quella storia del terminale, Linux, ehm.. cioè… lasciate che vi spieghi un attimo…

I computer sono incredibilmente veloci, accurati e stupidi. Gli uomini sono incredibilmente lenti, inaccurati e intelligenti. L’insieme dei due costituisce una forza incalcolabile.

A. Einstein

Salvo casi particolari per quanto riguarda gli umani, lo zio Albert ha ragione.

Cirque du Soleil Istanbul 2012 Alegria 1200660 nevit

“Flessibilità” (Fonte: Wikimedia Commons)

Bisogna solo imparare ad impartire a queste stupide scatolette sibilanti i comandi necessari a risparmiarci compiti noiosi e ripetitivi.

Ma perché bisognerebbe darsi pena a trafficare con la scrittura di oscuri codici, chiamati dagli adepti “script“, quando esistono tanti bei programmi con interfaccia grafica, finestrelle e bottoncini colorati? Flessibilità, ecco perché.

In molti casi, inoltre, finestrelle & bottoncini non sono altro che un “tramite” grafico per comandare un software che delle suddette non avrebbe alcun bisogno.

Imparando anche poche semplici “regole” si possono mettere insieme di volta in volta degli script (o singoli comandi) che rispondono a specifiche esigenze, combinando l’uso degli strumenti già presenti praticamente in ogni distribuzione Linux.


Ecco gli elefanti!

Vediamo ora un semplice script d’esempio con un compito molto interessante e divertente: “cantare” una canzoncina per bambini! È uno script costruito appositamente per mostrare alcune tra le funzioni disponibili più utili e più frequentemente impiegate.

Se volete provarlo subito, niente di più facile. Basta solo:

  1. copiare il listato in un file di testo e salvarlo, per esempio come “elefanti.sh
  2. renderlo eseguibile con chmod +x elefanti.sh
  3. lanciarlo da terminale con ./elefanti.sh (dalla cartella dove è salvato) o ./percorso/completo/elefanti.sh da qualunque altra posizione.
#!/bin/bash
# Canta insieme a me!
secondariga="sopra il filo di una ragnatela" # Una "variabile" per un testo...
num=0 # ...ed un'altra per un numero.
for i in Un Due Tre Quattro Cinque Sei Sette Otto Nove Dieci # Per ogni elemento della lista...
do
num=$(($num+1))    # Un po' di matematica
if [ "$i" = "Un" ] # Se c'è un solo elefante
then               # allora
elef="e"           # usiamo le desinenze
n=""               # delle parole
o="ò"              # al singolare
else               # altrimenti
elef="i"           # usiamo le desinenze
n="n"              # delle parole
o="aron"           # al plurale.
fi                 # Qui finiscono le condizioni.
echo $i! \($num\) | tr [:lower:] [:upper:] # Qui non si capisce un "pipe"!
echo $i elefant$elef si dondolava$n             # Scrivi,...
echo $secondariga                               # ...scrivi ancora,...
echo e trovando la cosa interessante,           # ...ancora un po',...
echo -e and$o a chiamare un altro elefante.\\n  # ...adesso basta.
# Cadenza test
sleep 1 # Fai un pisolino di 1 secondo!
# Cadenza normale
#sleep 15
done
exit # Esci, vai a prendere un po' d'aria

Ed eccone il risultato (sintetizzato) per vedere cosa può fare questo mucchio di parole senza senso qui sopra:

UN! (1)
Un elefante si dondolava
sopra il filo di una ragnatela
e trovando la cosa interessante,
andò a chiamare un altro elefante.

...
DIECI! (10)
Dieci elefanti si dondolavan
sopra il filo di una ragnatela
e trovando la cosa interessante,
andaron a chiamare un altro elefante.

Eh sì, pare che questa roba riesca davvero a “cantare” per bene la canzoncina degli elefanti, azzeccando anche le parole. Adesso vediamo il funzionamento in dettaglio però, per carità! non la cantate così ai vostri bimbi!🙂


Come funzionano gli elefanti

Riga 1: Innanzitutto informiamo il sistema operativo che le istruzioni contenute nello script devono essere interpretate (eseguite) dalla shell bash piuttosto che da un’altra delle shell disponibili. La shell non è altro che il famigerato terminale e bash è la shell più usata. Sul mio pc le shell disponibili sono due: bash e dash (le altre sono collegamenti alle shell effettivamente presenti). La busybox è quella che vedi quando qualcosa è andato abbastanza a donnine allegre quindi sperate di non averci mai a che fare.🙂

$ cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
$ ls -l /bin/*sh*
-rwxr-xr-x 1 root root 920796 set 19  2012 /bin/bash
-rwxr-xr-x 1 root root  96232 ago 15  2012 /bin/dash
lrwxrwxrwx 1 root root      4 dic 28 02:36 /bin/rbash -> bash
lrwxrwxrwx 1 root root      4 dic 28 02:36 /bin/sh -> dash
lrwxrwxrwx 1 root root      4 dic 28 02:36 /bin/sh.distrib -> dash
lrwxrwxrwx 1 root root      7 nov 16 18:58 /bin/static-sh -> busybox

La dichiarazione della shell da usare avviene sempre nella prima riga, usando la combinazione speciale “#!” seguita dal percorso completo della shell. Quando lo script contiene solo una serie di comandi (per esempio l’esecuzione dell’aggiornamento di sistema) tale indicazione può essere omessa. In questo caso invece, poiché lo script contiene anche delle strutture di controllo dell’esecuzione, la dichiarazione è necessaria in quanto potrebbero esserci differenze nella sintassi dei comandi a seconda della shell usata.

Riga 2: A differenza del precedente “#!“, il solo carattere “#” (hash) introduce un commento. I commenti vengono ignorati nell’esecuzione dello script ma sono ugualmente preziosi perché servono ad aggiungere informazioni utili: cosa fa lo script, chi lo ha creato, eventuali miglioramenti rispetto alle versioni precedenti e così via. Oltre che su righe “dedicate”, i commenti possono anche essere in linea con i comandi, come si vede nelle righe successive. Vale sempre la regola che tutto ciò che è preceduto dal “#” non è considerato come un’istruzione da eseguire.

Per scopi dimostrativi ho un po’ abusato dei commenti per documentare il funzionamento dello script: in pratica si capisce cosa accade ad ogni riga anche senza guardare il “vero” codice.

Righe 3-4: Fondamentale in ogni linguaggio di programmazione è l’uso delle variabili, “scatoline” all’interno delle quali mettere dati utili durante l’esecuzione dei programmi. Per assegnare un valore ad una variabile la sintassi è nomevariabile=valore mentre per utilizzarla all’interno dello script, cioè operare sul valore in essa contenuto, è necessario far precedere il nome dal simbolo “$“.

Righe 5-27: Uno dei “cicli” più frequentemente utilizzati è il ciclo “for“. Con questo costrutto si può far eseguire una serie di azioni su una lista di valori che vengono assegnati ad ogni iterazione alla variabile scelta come controllo. Qui la variabile “i” prenderà di volta in volta i valori della lista. La lista di valori si può costruire in diversi modi, rendendo questo ciclo molto versatile. Ad esempio:

  • tutti i file di un certo tipo: for f in *.txt
  • tutti i file contenuti in una cartella: for g in `ls /percorso/completo`
  • un intervallo di valori (ad esempio da 1 a 10): for t in {1..10}

Una volta definita la lista, tutte le azioni da eseguire sono comprese tra il do iniziale (riga 6) ed il done finale (riga 27).

Riga 7: La shell può anche eseguire operazioni matematiche seppure solo su numeri interi. La sintassi per “far di conto” è la seguente: $((<operazione>))

L’istruzione su questa riga dice allo script: incrementa di 1 il valore corrente di num (quindi ci vuole il “$“) ed assegna il nuovo valore alla stessa variabile.

Righe 8-17: Altro costrutto classico ed indispensabile: if... then... else... fi. Cioè SE si verifica la condizione che indico ALLORA fai alcune cose ALTRIMENTI fanne altre. Il “fi” (“if” scritto al contrario!) serve come delimitatore del blocco di istruzioni, analogamente al do... done del ciclo for.

Al primo “giro” definito dal ciclo for (quando “i” ha valore “Un”) dovrò avere tutto al singolare, successivamente tutto al plurale: tramite questo controllo lo script si “accorge” del cambiamento ed assegna i valori giusti alle variabili che rappresentano le desinenze di alcune parole della filastrocca.

Righe 18-22: Messe a posto le desinenze, possiamo visualizzare il testo della filastrocca con l’istruzione echo. Come si vede tale istruzione può sia stampare una stringa di testo esplicitamente scritta dopo di essa (riga 21) sia il contenuto di variabili, come avviene in tutte le altre righe del blocco.

Riga 17: Un piccolo salto indietro per spiegare il cosiddetto “piping” dei comandi. Letteralmente si possono collegare più comandi in sequenza tramite “tubi” che incanalano l’output del precedente verso il successivo. Il mezzo o meglio la sintassi necessaria è semplicemente il carattere “|”, detto appunto “pipe“:

comando1 | comando2 | ... | comandoN

Così posso avere elaborazioni successive dei risultati parziali dei comandi della sequenza fino ad ottenere un particolare risultato. In questo caso converto in MAIUSCOLO tramite il comando tr la riga, proveniente dal precedente comando echo, contenente il valore $i (Un -> UN; Due -> DUE; …) e $num (che è un numero e quindi non varia nella conversione).

Riga 22: Fatto il salto indietro, ritorniamo all’ultima riga del blocco per svelare il mistero “alla Kazzenger” dei caratteri scomparsi. I più pedan attenti avranno notato che già nella riga 17 c’erano un mucchietto di caratteri “\” di cui non c’è traccia nel testo stampato dallo script. Il backslash “\” serve proprio ad indicare che il carattere che lo segue deve essere interpretato letteralmente e non nella sua funzione di carattere “speciale”, il cui utilizzo può alterare l’esecuzione delle istruzioni: tale indicazione si chiama “escaping”. Uno degli esempi sono appunto le parentesi “(” “)” della riga 17 che, in particolare, servono a racchiudere una lista di valori. Senza l’escaping la shell si aspetta di trovare informazioni coerenti con la funzione dei caratteri speciali; non trovandole le prende un gran mal di testa ed interrompe l’esecuzione. Quando poi echo deve interpretare una sequenza speciale e non un singolo carattere, gli si aggiunge l’opzione “-e” (che sta, guarda un po’, per “escape”). La sequenza speciale è il “\n“, che rappresenta una nuova linea (“a capo”), preceduto (escape! :-)) dal “\“. L’alternativa è racchiudere i caratteri speciali tra virgolette o apici.

Righe 23-26: Concediamoci un po’ di pausa ogni tanto! L’istruzione sleep serve proprio a mettere a dormire (in attesa, se preferite) l’esecuzione per un tempo stabilito: 1 o 15 secondi in questo script che costituiscono l’intervallo tra una strofa e la successiva. L’istruzione “sleep 15” (riga 26) mostra un altro modo di usare i commenti, o meglio il “trucco” di trasformare in commenti tutte le istruzioni che non vogliamo far eseguire, senza cancellarle dal listato. Questo consente di far coesistere procedure alternative, lasciando attiva quella che ci sembra migliore, oppure di tenere “in sospeso” codice abbozzato o non funzionante in attesa di miglioramenti.

Riga 28: L’istruzione exit chiude lo script: tutto ciò che segue non viene elaborato. Si può comunque inserire questa istruzione anche in altri punti per far terminare l’esecuzione a seguito di particolari condizioni (ciclo if).


Come fare un elefante

Non sia mai che non lasci cinque-seimila pagine di roba da leggere per documentarsi (e c’è altro ancora…)!🙂 A parte le pagine man (a partire da man bash) degli specifici comandi, comincio col consigliare il classico manualone: la Advanced Bash Scripting Guide (sito dell’autore) è per me IL riferimento per lo scripting. È una guida molto completa e chiara, riccamente corredata di esempi pratici, ottima per imparare. Imperdibile.

Altra ottima fonte di informazioni soprattutto per chi cerca soluzioni “pronte” è Stackoverflow, “il sito di domande e risposte per programmatori professionisti ed appassionati”. Il livello medio dei suoi frequentatori è decisamente alto e, di conseguenza, quello delle risposte che si possono ottenere o trovare.

Se invece siete più sul social c’è @climagic che mostra usi creativi ed elaborati della riga di comando su Twitter (ed in alcuni video sul suo canale YouTube). Qualche altro account cinguetta sull’argomento allo stesso modo ma, visto che su Twitter ci sono sempre meno, vi tocca sgranchirvi i ditini e cercarli da soli. Comunque ci sono, eh!, li ho visti, parola di boyscout (che non ho mai fatto ma si dice così…).

Dulcis in fundo, la serie di post del “Progetto Bash” su The Secrets of Ubuntu (qui l’introduzione) che trattano in maniera più ordinata e sistematica gli elementi dello scripting in bash.

E questo è quanto:

if [ "$post" = "finito" ]
then
exit # Basta!!! Non se ne può più!
else
exit # Insisto...
fi
exit # A prescindere...

😀

Informazioni su Man from Mars

https://extendedreality.wordpress.com/

  1. obo

    Sei un genio, sei un fottutissimo genio *-* mi sto divertendo un mondo a cantarla ti giuro ahahahahaahah

    Mi piace

    • Eh eh😀 Grazie, è stato divertente scrivere questo scriptino, ma soprattutto vederlo in azione per la prima volta ha fatto ridere parecchio anche me!
      E poi speravo molto che stimolare il bambinone che c’è in noi avrebbe funzionato!

      Mi piace

  2. Pingback: Se sei felice e tu lo sai lancia uno script | Extended Reality

  3. Pingback: One elephant ran a script in bash… | Extended Reality

Dimmi che ne pensi o fai "Ciao ciao!" con la manina // Share your thoughts or just say "Hello!"

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

%d blogger cliccano Mi Piace per questo: