Gentoo Logo

Disclaimer : La versione originale di questo articolo è stata pubblicata da IBM developerWorks ed è di proprietà di Westtech Information Services. Questo documento è una versione aggiornata dell'articolo originale, e contiene numerosi miglioramenti apportati dal Gentoo Linux Documentation team.
Questo documento non è mantenuto attivamente.


Bash per esempi, Parte 1

Indice:

1.  Fondamenti di programmazione nella Bourne again shell (bash)

Introduzione

Forse vi chiederete per quale motivo dovreste imparare a programmare in Bash. Bè, ecco un paio di ragioni.

La state già eseguendo

se controllate, probabilmente scoprirete che in questo momento state eseguendo bash. Anche se avete cambiato la vostra shell di default, probabilmente bash sta ancora girando da qualche parte sul vostro sistema, dato che è la shell standard di Linux ed è usata per una grande varietà di scopi. Visto che bash sta già girando, ogni script bash aggiuntivo che eseguite sarà di conseguenza efficiente riguardo all'utilizzo di memoria, dato che condividerà la memoria con tutti gli altri processi bash già attivi. Perché caricare un interprete di 500K quando avete qualcosa già in esecuzione che fa lo stesso lavoro, e lo fa bene?

La state già usando

Non solo state già eseguendo bash, ma in realtà interagite quotidianamente con essa. Bash è sempre lì, quindi vale la pena imparare ad usarla sfruttandone appieno le potenzialità. Fare ciò renderà la vostra esperienza con bash più divertente e produttiva. Ma per quale motivo dovreste imparare a programmare in bash? Semplice: perché già pensate in termini di eseguire comandi, CPare file, effettuare il piping e redirezionare un output. Non dovreste imparare un linguaggio che vi consenta di utilizzare questi potenti costrutti, che vi fanno risparmiare tempo e che già sapete utilizzare? Le shell di comando liberano il potenziale di un sistema UNIX, e bash è la shell di Linux. È il collegamento ad alto livello tra voi e la macchina. Aumentate la vostra conoscenza di bash, e aumenterete automaticamente la vostra produttività in Linux e UNIX: è così semplice!

Confusione bash

Imparare bash in maniera sbagliata può creare grande confusione. Molti newbies digitano man bash per vedere la man page di bash, trovandosi davanti solo una descrizione stringata e molto tecnica delle funzionalità della shell. Altri digitano info bash (per vedere la documentazione GNU), ma o viene visualizzata la stessa man page, oppure (se sono fortunati) appare loro una documentazione non molto più amichevole.

Mentre questo potrebbe essere in qualche modo disorientante per i novizi, la documentazione standard di bash non può dare tutto a tutti, e si concentra verso chi ha già familiarità con la programmazione della shell in generale. Ci sono sicuramente un sacco di eccellenti informazioni tecniche nella man page, ma la loro utilità per i principianti è limitata.

Ecco che entra in scena questa guida. In essa, vi mostrerò come utilizzare in pratica i costrutti di programmazione bash, cosicché sarete in grado di scrivere da soli i vostri script. Anziché dare descrizioni tecniche, vi fornirò spiegazioni nella lingua di tutti i giorni, in modo che impariate non solo cosa fa un certo costrutto, ma anche quando lo si dovrebbe usare. Entro la fine di questa guida di tre parti, sarete in grado di scrivere da soli i vostri intricati script bash, e sarete ad un livello in cui potrete usare agevolmente bash, potendo integrare le vostre conoscenze con la lettura (e la comprensione!) della documentazione standard. Cominciamo.

Variabili d'ambiente

In bash, come in quasi tutte le altre shell, l'utente può definire variabili d'ambiente, che sono memorizzate internamente come stringhe ASCII. Una delle cose più comode riguardo alle variabili d'ambiente è che esse sono parte del modello di processo UNIX. Questo significa che le variabili d'ambiente non sono solo esclusive degli script della shell, ma possono essere usate anche da programmi compilati normalmente. Quando "esportiamo" una variabile d'ambiente in bash, ogni programma che eseguiamo successivamente piò leggere le nostre impostazioni, che si tratti di uno script della shell oppure no. Un buon esempio è il comando vipw, che normalmente consente a root di editare il file delle password di sistema. Impostando la variabile d'ambiente EDITOR con il nome del vostro editor di testo preferito, potete configurare vipw affinché usi questo al posto di vi, il che è comodo se siete abituati a xemacs e non vi piace affatto vi.

Il modo standard per definire una variabile d'ambiente in bash è:

Codice 1.1: Definire una variabile d'ambiente

$ myvar='Questa è la mia variabile d'ambiente!'

Il comando qui sopra definisce una variabile chiamata "myvar", che contiene la stringa "Questa è la mia variabile d'ambiente!". Ci sono diverse cose da notare: innanzitutto, non c'è alcuno spazio alle estremità del segno "=": qualsiasi spazio produrrebbe un errore (provare per credere). La seconda cosa da notare è che, mentre potremmo fare a meno delle virgolette se stessimo definendo una parola singola, esse sono necessarie quando il valore della variabile d'ambiente è più di una singola parola (cioè contiene spazi o tabulazioni).

Nota: Per avere informazioni estremamente dettagliate su come dovrebbero essere usate le virgolette in bash, potreste consultare la sezione "QUOTING" della man page bash. L'esistenza di speciali sequenze di caratteri che vengono "espanse" (rimpiazzate) con altri valori, complica la gestione delle stringhe in bash. In questa guida noi tratteremo solo la virgolettazione usata più spesso.

In terzo luogo, mentre normalmente possiamo usare le doppie virgolette anziché i singoli apici, fare questo nell'esempio precedente avrebbe causato un errore. Perché? Perché l'utilizzo degli apici singoli disabilita una funzione bash chiamata espansione, che sostituisce caratteri speciali e sequenze di caratteri con valori. Ad esempio, il carattere "!" è il carattere di espansione della cronologia, e viene di solito sostituito da bash con un comando digitato precedentemente. (Non tratteremo l'espansione della cronologia in questa serie di articoli, in quanto essa non è usata frequentemente nella programmazione bash. Per maggiori informazioni su di essa, consultate la sezione "HISTORY EXPANSION" della man page bash.) Sebbene questa funzionalità simil-macro possa essere comoda, in questo momento noi vogliamo un punto esclamativo letterale alla fine della nostra variabile d'ambiente, non una macro.

Adesso, vediamo come possono essere usate in pratica le variabili d'ambiente. Ecco un esempio:

Codice 1.2: Usare variabili d'ambiente

$ echo $myvar
Questa è la mia variabile d'ambiente!

Facendo precedere il nome della nostra variabile d'ambiente da un $, possiamo fare in modo che bash la rimpiazzi con il valore di myvar. Nella terminologia bash, questo viene chiamato "espansione di variabile". Ma se proviamo quanto segue:

Codice 1.3: Primo tentativo di usare l'espansione di variabile

$ echo foo$myvarbar
foo

Volevamo che fosse visualizata la stringa "fooQuesta è la mia variabile d'ambiente!bar", ma non ha funzionato. Cosa è andato storto? In breve, la funzione di espansione di variabili bash si è confusa. Non capiva se doveva espandere la variabile $m, $my, $myvar, $myvarbar, ecc. Come possiamo essere più espliciti, dicendo chiaramente a bash a quale variabile ci stiamo riferendo? Proviamo questo:

Codice 1.4: Espansione di variabile corretta

$ echo foo${myvar}bar
fooQuesta è la mia variabile d'ambiente!bar

Come potete vedere, possiamo racchiudere il nome della variabile d'ambiente all'interno di parentesi graffe, nel caso in cui esso non sia chiaramente separato dal testo che lo circonda. Mentre $myvar è più veloce da digitare e funziona la maggiorparte delle volte, ${myvar} non darà quasi mai alcun problema. A parte questo, fanno tutte e due la stessa cosa, e infatti nel resto della guida vedrete entrambe le forme dell'espansione di variabili. Ricordate di usare la forma con le parentesi graffe quando la vostra variabile d'ambiente non è isolata dal testo che la circonda attraverso degli spazi bianchi (spazi o tabulazioni).

Abbiamo anche detto che possiamo "esportare" le variabili. Quando esportiamo una variabile d'ambiente, essa entra automaticamente nell'ambiente di ogni script o eseguibile eseguito successivamente. Gli script della shell riescono a raggiungere la variabile d'ambiente usando il supporto per le variabili d'ambiente interno alla shell, mentre i programmi scritti in C possono usare la chiamata di funzione getenv(). Ecco un po' di codice C di esempio che dovreste digitare e compilare; ci aiuterà a capire le variabili d'ambiente dal punto di vista di C:

Codice 1.5: myvar.c -- un esempio di programma C sulle variabili d'ambiente

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  char *myenvvar=getenv("EDITOR");
  printf("The editor environment variable is set to %s\n",myenvvar);
}

Salvate il codice sorgente qui sopra in un file chiamato myenv.c, e poi compilatelo con il comando:

Codice 1.6: Compilare il codice sorgente

$ gcc myenv.c -o myenv

Ora ci sarà un programma eseguibile nella vostra directory, il quale, se eseguito, visualizzerà il valore della variabile d'ambiente EDITOR, se esiste. Ecco cosa succede quando lo eseguo sulla mia macchina:

Codice 1.7: Eseguire il programma compilato

$ ./myenv
The editor environment variable is set to (null)

Hmmm... visto che la variabile d'ambiente EDITOR non era stata settata, il programma ha dato come output una stringa null. Proviamo a settarla ad un valore specifico:

Codice 1.8: Proviamo con un valore specifico

$ EDITOR=xemacs
$ ./myenv
The editor environment variable is set to (null)

Mentre vi sareste aspettati che myenv visualizzasse il valore "xemacs", in realtà così non è stato, perché non abbiamo esportato la variabile d'ambiente EDITOR. Questa volta, lo faremo funzionare:

Codice 1.9: Lo stesso programma dopo aver esportato la variabile

$ export EDITOR
$ ./myenv
The editor environment variable is set to xemacs

Quindi, avete visto con i vostri occhi che un altro processo (in questo caso il nostro programma C di esempio) non può vedere la variabile d'ambiente finché essa non viene esportata. Di fatto, se volete, potete definire ed esportare una variabile d'ambiente usando un'unica riga, in questo modo:

Codice 1.10: Definire ed esportare una variabile d'ambiente in un unico comando

$ export EDITOR=xemacs

Questo funzionerà in modo identico alla versione a due righe. Ora è il momento di vedere come cancellare una variabile d'ambiente usando unset:

Codice 1.11: Cancellare la variabile

$ unset EDITOR
$ ./myenv
The editor environment variable is set to (null)

Panoramica sul taglio delle stringhe

Tagliare le stringhe -- ovvero, spezzare una stringa originale in parti più piccole e separate -- è uno di quei compiti che sono svolti ogni giorno dalla maggiorparte degli script della shell. Molte volte, gli script della shell devono prendere un percorso completo e trovare il file o la directory finale. Mentre è possibile (e divertente!) codificare ciò in bash, l'eseguibile UNIX basename esegue l'operazione estremamente bene:

Codice 1.12: Usare basename

$ basename /usr/local/share/doc/foo/foo.txt
foo.txt
$ basename /usr/home/drobbins
drobbins

basename è un tool abbastanza comodo per spezzare le stringhe. Il suo compagno, chiamato dirname, ci dà l'"altra" parte del percorso, quella eliminata da basename:

Codice 1.13: Usare dirname

$ dirname /usr/local/share/doc/foo/foo.txt
/usr/local/share/doc/foo
$ dirname /usr/home/drobbins/
/usr/home

Nota: Sia dirname che basename non cercano alcun file o directory sul disco: sono solo comandi di manipolazione delle stringhe.

Sostituzione di comandi

Una cosa molto utile da sapere è come creare una variabile d'ambiente che contenga il risultato di un comando eseguibile. Questo è molto semplice da fare:

Codice 1.14: Creare una variabile d'ambiente contenente il risultato di un comando

$ MYDIR=`dirname /usr/local/share/doc/foo/foo.txt`
$ echo $MYDIR
/usr/local/share/doc/foo

Quello che abbiamo fatto sopra è chiamato sostituzione di comando. Vale la pena di esaminare diverse cose in questo esempio. Nella prima riga, abbiamo semplicemente incluso il comando che volevamo eseguire all'interno di apici inversi. Non si tratta degli apici singoli standard, ma si ottengono premendo AltGr contemporaneamente al tasto dell'apostrofo. Possiamo fare esattamente la stessa cosa con la sintassi alternativa bash per la sostituzione di comando:

Codice 1.15: Sintassi alternativa per la sostituzione di comando

$ MYDIR=$(dirname /usr/local/share/doc/foo/foo.txt)
$ echo $MYDIR
/usr/local/share/doc/foo

Come potete vedere, bash fornisce molti modi per fare esattamente la stessa cosa. Usando la sostituzione di comando, possiamo mettere ogni comando, o pipeline di comandi, tra ` ` o $( ), assegnandoli poi ad una variabile d'ambiente. È comodo! Ecco un esempio di come si può usare una pipeline con la sostituzione di comando:

Codice 1.16: Sostituzione di comando in una pipeline

$ MYFILES=$(ls /etc | grep pa)
$ echo $MYFILES
pam.d passwd

Tagliare stringhe da professionisti

Se basename e dirname sono tools grandiosi, ci sono volte in cui potremmo avere necessità di effettuare operazioni di taglio stringhe più avanzate di semplici manipolazioni di percorsi standard. In questi casi, possiamo servirci della funzionalità avanzata di espansione di variabili interna a bash. Abbiamo già usato il tipo standard di espansione di variabili, che appare così: ${MYVAR}. Ma bash può anche eseguire, da solo, alcune comodi tagli di stringhe. Date un'occhiata a questi esempi:

Codice 1.17: Esempi di tagli stringhe

$ MYVAR=foodforthought.jpg
$ echo ${MYVAR##*fo}
rthought.jpg
$ echo ${MYVAR#*fo}
odforthought.jpg

Nel primo esempio, abbiamo digitato ${MYVAR##*fo}. Cosa significa esattamente? In pratica, dentro il ${ }, abbiamo digitato il nome della variabile d'ambiente, due ##, e una wildcard ("*fo"). Poi, bash ha preso MYVAR, ha trovato la sottostringa più lunga, dall'inizio della stringa "foodforthought.jpg", che corrispondesse alla wildcard "*fo", e ha tolto la parte iniziale della stringa. È un concetto un po' difficile da afferrare all'inizio, quindi, per vedere meglio come funziona questa opzione speciale "##", vediamo passo passo come bash ha completato questa espansione. Innanzitutto, ha iniziato cercando le sottostringhe all'inizio di "foodforthought.jpg" che soddisfacessero la wildcard "*fo". Ecco le sottostringhe che ha controllato:

Codice 1.18: Sottostringhe controllate

f
fo              SODDISFA *fo
foo
food
foodf
foodfo          SODDISFA *fo
foodfor
foodfort
foodforth
foodfortho
foodforthou
foodforthoug
foodforthought
foodforthought.j
foodforthought.jp
foodforthought.jpg

Dopo avere cercato le sottostringhe che corrispondessero alla wildcard, come potete vedere bash ne ha trovate due. A questo punto ha scelto la sottostringa più lunga, l'ha rimossa dall'inizio della stringa originale, e infine ha fornito il risultato.

La seconda forma dell'espansione di variabile mostrata sopra appare identica alla prima, con la differenza che usa un solo "#", e bash esegue un procedimento quasi identico. Essa controlla lo stesso set di sottostringhe del primo esempio, ma questa volta rimuove dalla stringa originale la sottostringa più breve, e dà il risultato. Quindi, non appena controlla la sottostringa "fo", la rimuove dalla nostra stringa, dando come risultato "odforthought.jpg".

Questo potrebbe sembrare estremamente criptico, quindi vi mostrerò un modo semplice per ricordare questa funzionalità. Quando cercate l'opzione più lunga, usate ## (perché ## è più lungo di #). Quando cercate l'opzione più corta, usate #. Vedete, non è così difficile da ricordare, dopotutto! Ecco alcuni veloci esempi su come tagliare la parte finale delle stringhe:

Codice 1.19: Tagliare la parte finale di una stringa

$ MYFOO="chickensoup.tar.gz"
$ echo ${MYFOO%%.*}
chickensoup
$ echo ${MYFOO%.*}
chickensoup.tar

Come potete vedere, le opzioni di espansione di variabili % e %% funzionano esattamente come # e ##, con la differenza che esse rimuovono la sottostringa corrispondente alla wildcard dalla fine della stringa. Si noti che non serve usare il carattere "*" se si desidera rimuovere una specifica sottostringa dalla fine:

Codice 1.20: Rimuovere sottostringhe dalla fine

MYFOOD="chickensoup"
$ echo ${MYFOOD%%soup}
chicken

In questo esempio, non importa se usiamo "%%" o "%", dato che è possibile una sola opzione.

Possiamo usare un'altra forma di espansione di variabile per selezionare una sottostringa specifica, basata su uno specifico carattere di inizio e su una data lunghezza. Provate a digitare le seguenti righe in bash:

Codice 1.21: Selezionare una sottostringa specifica

$ EXCLAIM=cowabunga
$ echo ${EXCLAIM:0:3}
cow
$ echo ${EXCLAIM:3:7}
abunga

Questa forma di separazione di stringhe può risultare abbastanza comoda: specificate semplicemente il carattere da cui iniziare e la lunghezza della sottostringa, separando il valori con i ':'.

Applicare il taglio delle stringhe

Adesso che abbiamo imparato tutto sul taglio delle stringhe, scriviamo un piccolo e semplice script della shell. Il nostro script accetterà un singolo file come argomento, e ci dirà se esso sembra essere un tarball oppure no. Per determinare se si tratti di un tarball, lo script cercherà l'estensione ".tar" alla fine del file. Eccolo:

Codice 1.22: mytar.sh -- uno script di esempio

#!/bin/bash

if [ "${1##*.}" = "tar" ]
then
       echo Questo sembra essere un tarball.
else
       echo A prima vista, questo non sembra essere un tarball.
fi

Per eseguire lo script, mettetelo in un file chiamato mytar.sh, e digitate chmod 755 mytar.sh per renderlo eseguibile. Poi, provatelo su un tarball, in questo modo:

Codice 1.23: Provare lo script

$ ./mytar.sh thisfile.tar
Questo sembra essere un tarball.
$ ./mytar.sh thatfile.gz
A prima vista, questo non sembra essere un tarball.

OK, funziona, ma non serve a molto. Prima di renderlo più utile, diamo un'occhiata al costrutto "if" usato sopra. In esso, abbiamo un'espressione booleana. In bash, l'operatore di paragone "=" controlla l'uguaglianza delle stringhe. In bash, tutte le espressioni booleane sono racchiuse in parentesi quadre. Ma cosa testa in realtà l'espressione booleana? Diamo un'occhiata sul lato sinistro. Secondo quello che abbiamo imparato dal taglio delle stringhe, "${1##*.}" rimuoverà la sottostringa più lunga che soddisfi "*.", dall'inizio della stringa contenuta nella variabile d'ambiente "1", dando il risultato. Questo farà sì che venga considerata tutta la parte dopo l'ultimo "." nel nome del file. Ovviamente, se il nome finisce con ".tar", otterremo "tar" come risultato, e la condizione sarà vera.

Innanzitutto potreste chiedervi cos'è la variabile d'ambiente "1". Molto semplice: $1 è il primo argomento dello script, $2 è il secondo, ecc. OK, ora che abbiamo esaminato la funzione, possiamo dare una prima occhiata ai costrutti "if".

Costrutti "if"

Come la maggiorparte delle lingue, bash ha una sua forma di condizionale. Quando lo utilizzate, attenetevi al formato mostrato sopra, cioè tenete "if" e "then" su righe separate, e tenete "else" e il fi" finale in allineamento orizzontale con essi. Questo rende il codice più facile da leggere e semplifica la correzione di eventuali bug. In aggiunta alla forma "if,else", ci sono diverse altre forme di costrutti "if":

Codice 1.24: Forma base di costrutto if

if      [ condizione ]
then
        azione
fi

Questo costrutto esegue un'azione solo se la condizione è vera; altrimenti non esegue alcuna azione e continua ad eseguire le eventuali righe successive al "fi".

Codice 1.25: Controllare una condizione prima di continuare con i comandi che seguono fi

if [ condizione ]
then
        azione
elif [ condizione2 ]
then
        azione2
.
.
.
elif [ condizione3 ]
then

else
        azionex
fi

La forma "elif" testerà una dopo l'altra tutte le condizioni, eseguendo l'azione corrispondente alla prima condizione vera. Se nessuna delle condizioni risulta vera, eseguirà l'azione "else", se presente, dopodiché continuerà ad eseguire le eventuali righe che seguono il costrutto "if,elif,else".

La parte successiva

Ora che abbiamo trattato le funzionalità bash più elementari, è il momento di prepararsi a scrivere qualche vero script. Nel prossimo articolo, tratterò i costrutti di looping, le funzioni, il namespace, e altri argomenti essenziali. Poi, saremo pronti a scrivere alcuni script più complicati. Nel terzo articolo, ci concentreremo quasi esclusivamente su script e funzioni molto complesse, nonché su diverse opzioni di design degli script bash. Ci vediamo!

2.  Risorse

Link utili



Stampa

Aggiornato il 9 ottobre 2005

Oggetto: Imparando come programmare nel linguaggio di scripting bash, la vostra interazione giornaliera con Linux diventerà più divertente e produttiva, e sarete in grado di costruire, con questi standard, costrutti UNIX (come pipelines e redirezioni) che già conoscete e amate. In questa guida di tre parti, Daniel Robbins vi insegnerà come programmare in bash attraverso degli esempi. Egli tratterà le basi più elementari (rendendo questa guida eccellente per i principianti), ma anche le funzioni più avanzate, man mano che si andrà avanti.

Daniel Robbins
Autore

Cristiano Chiucchiolo
Traduzione

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.