Programmazione della shell Bash

Variabili, variabili d'ambiente e variabili speciali

Come ogni linguaggio che si rispetti anche Bash possiede il concetto di variabile, ossia di area di memoria della macchina, identificata da un nome ben preciso, definito dalla shell o dall'utente, in cui possono essere memorizzate delle informazioni. Con Bash, come con altri linguaggi di scripting e shell del sistema operativo, è necessario però distinguere fra tre tipi diversi di variabili: le variabili “semplici”, le variabili “d'ambiente”, condivise fra più programmi eseguiti nell'ambito della medesima shell e variabili “speciali”, il cui contenuto ha un significato ben preciso che condiziona il funzionamento della shell o l'esecuzione di determinati programmi.

Variabili scalari e array

Per definire una variabile nell'ambito di una shell Bash o di uno script è sufficiente utilizzare l'operatore di assegnazione “=” con la sintassi “variabile=valore”. Il nome della variabile deve iniziare con un carattere non numerico e non deve contenere spazi o altri caratteri con un significato speciale per Bash (come, ad esempio, i caratteri “=”, “*”, “$” o “&”). Se il valore da assegnare alla variabile contiene degli spazi, questi devono essere preceduti dal carattere “\”, oppure l'intera stringa da assegnare alla variabile deve essere delimitata da singoli o doppi apici. Di seguito riportiamo alcuni esempi.

$ a=17
$ nome='Marco Liverani'
$ filename=nome\ file

È bene osservare che nell'assegnare un valore ad una variabile non devono esserci spazi tra il nome della variabile e l'operatore di assegnazione “=” e tra questo ed il valore della variabile.

Dopo averne definito il valore, è possibile utilizzare una variabile in un'espressione o in un'altra istruzione. Per riferirsi ad una variabile e per distinguere il nome da una qualsiasi altra stringa di caratteri, il nome deve essere preceduto dal simbolo “$”, con un'espressione del tipo “$variabile”; in alternativa si può anche utilizzare l'espressione “${variabile}”, sostanzialmente equivalente alla precedente nel caso delle variabili scalari.

Ad esempio, utilizzando il comando “echo” per visualizzare in output un'espressione è possibile visualizzare il valore assegnato precedentemente ad una variabile:

$ echo $nome
Marco Liverani
$ echo ${filename}
nome file

Con l'espressione “${#variabile}” si ottiene la lunghezza in caratteri dell'espressione assegnata alla variabile. Ad esempio:

$ echo ${#filename}
9

Ricordiamo che una variabile si dice “scalare” se può contenere un solo valore per volta. In alternativa, per memorizzare un insieme di valori, Bash mette a disposizione la struttura dati di array, con cui è possibile memorizzare una sequenza di informazioni. Di fatto un array è una collezione di variabili scalari denominate con lo stesso nome e distinte l'una dall'altra mediante un indice numerico intero non negativo; il valore dell'indice va da zero, per identificare il primo elemento della sequenza memorizzata nell'array, fino ad n−1, indicando con n il numero di elementi presenti nella sequenza.

Per definire esplicitamente un array si può utilizzare l'espressione “array=(valore0 valore1valoren−1)”. Se i valori sono costituiti da stringhe contenenti degli spazi, tali valori devono essere delimitati da apici o doppi apici. Ad esempio è possibile definire degli array di valori con i seguenti comandi:

$ a=(10 20 30 40)
$ rapaci=(aquila 'falco pellegrino' allocco gheppio)

Per referenziare i singoli valori di un array si usa l'espressione “${array[i]}”; naturalmente i è il numero intero che identifica l'indice dell'elemento dell'array a cui ci si vuole riferire, con 0 ≤ i < n in un array con n elementi. Facendo riferimento all'esempio precedente, potremo eseguire i seguenti comandi che possono aiutare a capire la sintassi di Bash nell'uso degli array:

$ echo $rapaci
aquila
$ echo ${rapaci[0]}
aquila
$ echo ${rapaci[1]}
falco pellegrino
$ a=${rapaci[1]}
$ echo $a
falco pellegrino
$ a=(${rapaci[1]})
$ echo ${a[1]}
pellegrino

Con l'espressione “${#array[*]}” si ottiene il numero di elementi presenti in un array; l'indice del primo elemento dell'array è 0, mentre l'indice dell'ultimo elemento è ${#array[*]}−1. Ad esempio:

$ echo ${#rapaci[*]}
4

Infine, per riferirsi a tutti gli elementi di un array si può scrivere “${array[*]}”; così, ad esempio, per stampare tutti gli elementi di un array o per copiare tutto il contenuto di un array in una variabile scalare come un'unica stringa, si possono utilizzare le seguenti istruzioni:

$ echo ${rapaci[*]}
aquila falco pellegrino allocco gheppio
$ rap=${rapaci[*]}
$ echo $rap
aquila falco pellegrino allocco gheppio

Variabili d'ambiente

Il termine shell (“conchiglia”, in inglese) rende bene l'idea di un involucro che “avvolge” il suo contenuto e lo scherma dall'ambiente esterno; all'interno dell'involucro “vivono” variabili, funzioni e processi, che sono in parte invisibili all'esterno dell'involucro stesso. Definendo una variabile ed assegnandogli un valore con l'operatore “=”, la sua visibilità è limitata alla shell in cui è stata definita; eseguendo uno script che opera in un'istanza della shell differente, la variabile non sarà visibile nella seconda shell e quindi il suo valore non sarà noto allo script eseguito in questo secondo ambiente. Un esempio aiuterà forse a comprendere meglio questo aspetto:

$ a=1
$ bash
$ echo $a

$ exit
$ echo $a
1

Una variabile d'ambiente è una variabile definita in modo da essere resa visibile alla shell in cui è stata definita e a tutte le shell eseguite da quest'ultima. Per definire una variabile d'ambiente si deve usare il comando “export”. Questo comando può essere usato per definire una variabile d'ambiente e contestualmente assegnarle un valore, oppure semplicemente per definire come variabile d'ambiente una variabile, senza assegnarle un valore (che può essere assegnato prima o dopo l'uso del comando “export”). Nella stessa istruzione possono essere definite numerose variabili d'ambiente elencandole nella stessa riga di comando, separate l'una dall'altra da uno spazio.

L'uso del comando è il seguente:

export variabile1 variabile2 ...

oppure

export variabile1=valore1 variabile2=valore2 ...

Siccome il comando “export” si aspetta come argomento una sequenza di nomi di variabili, questi non devono essere preceduti dal simbolo “$”. Di seguito riportiamo un esempio d'uso del comando export:

$ export a=1
$ b=2
$ echo $a $b
1 2
$ bash
$ echo $a $b
1
$ b=3
$ echo $a $b
1 3
$ exit
exit
$ echo $a $b
1 2

Nell'esempio precedente si nota come il valore della variabile b non sia visibile alla seconda shell Bash, lanciata all'interno della prima (e completamente contenuta nella prima, come in un gioco di scatole cinesi). Inoltre, il valore assegnato a b nella sotto-shell non è visibile alla shell principale. Al contrario, la visibilità di a è estesa ad entrambe le shell, visto che a è una variabile d'ambiente.

Anche uno script viene eseguito in una shell diversa da quella attraverso la quale viene lanciato. Ad esempio consideriamo il seguente script elementare, salvato nel file “stampa.sh”:

   1  #!/bin/bash
   2  echo -n "Variabile a="
   3  echo $a

Nello script di esempio il valore della variabile a non viene definito, quindi ci si aspetta che questo sia stato definito prima di eseguire lo script. Naturalmente la variabile a dovrà essere definita come variabile d'ambiente, altrimenti lo script non potrà avere visibilità sul valore di quella variabile. Il seguente esempio illustra il comportamento della shell:

$ a=17
$ ./stampa.sh
Variabile a=
$ echo $a
17
$ export a
$ ./stampa.sh
Variabile a=17

Per visualizzare tutte le variabili d'ambiente impostate nella sessione corrente, è sufficiente digitare il comando “env”; un risultato analogo è possibile ottenerlo con il comando “export -p”. Nell'esempio seguente viene presentato un'estratto delle variabili d'ambiente definite nella mia sessione utente:

$ env
TERM=xterm
SHELL=/bin/bash
CVSROOT=/usr/local/CVSrepository
USER=marco
PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11/bin
PWD=/Users/marco
PS1=\$ 
HOME=/Users/marco
LOGNAME=marco
_=/usr/bin/env

Variabili speciali

La shell Bash dispone di alcune variabili “speciali” che consentono di modificare la configurazione della shell stessa o di alcuni programmi che operano nell'ambito della shell, oppure consentono di fornire alcune utili informazioni agli script in esecuzione nella shell. Distinguiamo quindi due tipi di variabili speciali:

Nella prima famiglia di variabili speciali rientrano, ad esempio, le variabili d'ambiente HOME, PS1 e PATH. La prima indica il path della home directory dell'utente, ossia della directory che viene resa attiva come directory di lavoro corrente al momento del login sul sistema e ogni volta che viene usato il comando “cd” senza alcun parametro aggiuntivo. Tipicamente, in ambiente UNIX, il valore della variabile HOME è impostato come “/home/username”, dove con “username” si intende l'identificativo dell'utente (ad esempio: “/home/marco” per l'utente “marco”).

La variabile PS1 contiene una stringa che viene visualizzata dalla shell come prompt per le sessioni interattive con l'utente. La stringa può essere una costante (es.: “export PS1='pippo '”) oppure può contenere dei “meta-caratteri” che consentono di costruire dinamicamente il prompt dei comandi. I meta-caratteri sono tutti preceduti dal carattere “\” (backslash); una elencazione completa dei meta-caratteri supportati per la creazione di un prompt “dinamico” si trova nella sezione “Prompting” della pagina di manuale della Bash\footnote{Ricordiamo che la pagina di manuale della Bash, sui sistemi UNIX, può essere visualizzata con il comando “man bash”.}. Una sintesi di questi simboli viene proposta nella seguente Tabella.

Alcuni meta-caratteri per la definizione di un “prompt dinamico”
Meta-carattereDescrizione
\h il nome dell'host su cui viene eseguita la Bash
\u lo username dell'utente che sta eseguendo la Bash
\w il path completo della directory corrente
\W la directory corrente (priva del path completo)
\! il numero progressivo del comando nella sequenza dei comandi eseguiti nell'ambito della shell
\$ il carattere “#” se lo userid dell'utente è 0, il carattere “$” altrimenti (per evidenziare se la shell è eseguita come utente amministratore del sistema, root, o meno)

Il seguente esempio può far comprendere meglio il significato dei meta-caratteri per la definizione del prompt “dinamico”:

$ export PS1='pippo> '
pippo> export PS1='\u@\h:\w\$ '
marco@aquilante:~/src$ export PS1='\$ '
$

La variabile d'ambiente PATH, infine, contiene un elenco di directory, separate dal carattere “:”, in cui la shell cerca i comandi impartiti in modalità interattiva dall'utente o presenti in uno script, qualora tali comandi non siano invocati con un path assoluto o relativo alla directory corrente. Ad esempio, quando nella shell viene impartito il comando “ls”, l'interprete dei comandi, non riconoscendo il comando come interno alla shell, cerca il relativo file eseguibile in una delle directory elencate nella variabile PATH. In genere sui sistemi UNIX questo file eseguibile si trova nella directory “/bin”.

L'effetto della modifica del valore di queste variabili è immediato, ma viene perso al termine della sessione di lavoro. Per questo motivo spesso il valore di queste variabili d'ambiente viene definito nel file di configurazione della Bash, tipicamente nel file “~/.bashrc”.

Come abbiamo accennato in precedenza, altre variabili speciali forniscono utili informazioni sullo stato della shell; in questo caso il valore delle variabili viene gestito autonomamente dalla shell stessa e non può essere impostato con un comando impartito dall'utente. Le variabili di questo tipo sono molto numerose; in Tabella ne sono riportate solo alcune, con una descrizione sintetica.

Alcune variabili speciali gestite direttamente dalla shell
VariabileDescrizione
BASH_ARGC il numero di parametri forniti alla shell sulla riga di comando
BASH_ARGV un array contenente l'elenco dei parametri forniti alla shell sulla riga di comando (l'elenco è ordinato dall'ultimo al primo, come una pila)
BASH_VERSION il numero di versione della shell Bash
HOSTNAME il nome dell'host su cui viene eseguita la shell Bash
MACHTYPE il tipo di macchina (architettura hardware o CPU) su cui viene eseguita la shell Bash
OSTYPE il nome e la versione del sistema operativo su cui viene eseguita la shell Bash
PPID il process identifier (PID) del processo padre della shell Bash corrente
PWD la directory corrente in cui viene eseguita la shell Bash
RANDOM ogni volta che viene utilizzata questa variabile il suo valore cambia in modo casuale tra 0 e 215−1 = 32.767
UID lo user identifier, il numero progressivo che identifica univocamente l'utente che sta eseguendo la Bash

La variabile RANDOM consente di sfruttare il generatore di numeri interi pseudo-casuali della Bash: ogni volta che viene utilizzata la variabile il suo valore viene modificato in modo casuale. La sequenza di numeri pseudo-casuali viene inizializzata autonomamente dalla shell; tuttavia per riprodurre più volte una determinata sequenza, è possibile fornire un “seme” di inizializzazione assegnando un valore alla variabile RANDOM. Il seguente esempio (script “random.sh”) visualizza i primi due numeri casuali di una sequenza inizializzata autonomamente dalla Bash e poi i primi due numeri casuali della sequenza ottenuta utilizzando come seme il valore 10:

   1  #!/bin/bash
   2  echo "r1 = $RANDOM"
   3  echo "r2 = $RANDOM"
   4  RANDOM=10
   5  echo "r3 = $RANDOM"
   6  echo "r4 = $RANDOM"

Eseguendo lo script si ottiene il risultato riportato di seguito; lo script viene eseguito due volte per evidenziare il fatto che la sequenza di numeri casuali prodotta autonomamente dalla Bash (i primi due numeri r1 e r2) è costituita da numeri che con elevata probabilità sono sempre diversi, mentre la sequenza prodotta utilizzando un valore costante come “seme” (i numeri r3 e r4), è costituita sempre dagli stessi numeri.

$ ./random.sh
r1 = 18550
r2 = 29138
r3 = 4543
r4 = 28214
$ ./random.sh
r1 = 19458
r2 = 30487
r3 = 4543
r4 = 28214

Lanciando uno script è possibile specificare sulla riga di comando uno o più parametri (argomenti) separati l'uno dall'altro con uno spazio (se un parametro contiene degli spazi, deve essere delimitato con gli apici). Lo script può accedere ai parametri forniti sulla linea di comando a “run time”, utilizzando la variabile d'ambiente BASH_ARGV, un array dove i parametri sono memorizzati con una struttura a “pila”: questo significa che ogni parametro viene memorizzato all'inizio della struttura dati; quindi il primo dei parametri presenti sulla linea di comando si trova memorizzato come ultimo elemento dell'array, mentre l'ultimo dei parametri si trova nella prima posizione dell'array. Il seguente esempio aiuterà a comprendere questo meccanismo. Consideriamo il seguente shell script, memorizzato nel file “parametri.sh”:

   1  #!/bin/bash
   2  echo "Numero di parametri sulla riga di comando: $BASH_ARGC"
   3  echo "Primo elemento del vettore: ${BASH_ARGV[0]}"
   4  echo "Secondo elemento del vettore: ${BASH_ARGV[1]}"
   5  echo "Terzo elemento del vettore: ${BASH_ARGV[2]}"

Eseguendo lo script si ottiene il risultato seguente, in cui risulta che il valore della variabile ${BASH_ARGV[0]}, il primo elemento dell'array, è in realtà l'ultima delle stringhe digitate dall'utente sulla riga di comando e, viceversa, il valore di ${BASH_ARGV[2]}, l'ultimo degli elementi del vettore, è pari alla prima delle tre stringhe digitate dall'utente:

$ ./parametri.sh pippo pluto paperino
Numero di parametri sulla riga di comando: 3
Primo elemento del vettore: paperino
Secondo elemento del vettore: pluto
Terzo elemento del vettore: pippo

Altre variabili gestite autonomamente dalla shell consentono di ottenere informazioni sui parametri passati sulla linea di comando dello shell script o sul numero di processo (PID, process identifier) dello script stesso.

La variabile $$ contiene il PID (process ID) dello script in esecuzione; questa informazione potrebbe essere utile per definire il nome di un file temporaneo da creare in una directory, in modo tale da ridurre la probabilità di entrare in conflitto con il nome di un altro file creato dallo stesso script o da un altro programma. Ad esempio il seguente comando:

$ grep pippo *.c > /tmp/pippo.$$

consente di memorizzare l'output prodotto dal comando “grep” nel file “pippo.n”, dove n è il PID del processo che esegue il comando stesso. In questo modo lanciando due volte lo stesso comando saranno prodotti due file di output differenti (cambia il numero utilizzato come estensione del nome del file).

La variabile speciale $@ fornisce una concatenazione di tutti i parametri forniti allo script sulla riga di comando. La variabile $# restituisce invece il numero di parametri passati sulla riga di comando. Tali parametri, oltre che mediante l'array BASH_ARGV, sono accessibili anche mediante le variabili $1, $2, ecc. La variabile $0 invece, fornisce il comando con cui è stato eseguito lo script, privo dei parametri riportati sulla stessa riga di comando. I parametri sono memorizzati nelle variabili $1, $2, $3, ..., esattamente nell'ordine con cui compaiono sulla riga di comando.

Il comando “shift” consente di spostare di una posizione a sinistra tutti i parametri passati allo script sulla riga di comando, eliminando in questo modo il primo: dopo l'esecuzione del comando “shift” la variabile $1 conterrà il secondo dei parametri, la variabile $2 conterrà il terzo e così via. Con l'esecuzione di “shift” vengono modificati anche i valori delle variabili $# e $@. Il seguente script (file “parametri_bis.sh”) illustra il funzionamento del comando “shift” ed il significato delle variabili speciali:

   1  #!/bin/bash
   2  echo "Numero di parametri: $#"
   3  echo "Parametri: $@"
   4  echo "Comando: $0"
   5  echo "Primo: '$1', Secondo: '$2', Terzo: '$3'"
   6  shift
   7  echo "Numero di parametri: $#"
   8  echo "Parametri: $@"
   9  echo "Comando: $0"
  10  echo "Primo: '$1', Secondo: '$2', Terzo: '$3'"

Di seguito riportiamo l'output prodotto dallo script precedente:

$ ./parametri_bis.sh uno due tre
Numero di parametri: 3
Parametri: uno due tre
Comando: ./parametri_bis.sh
Primo: 'uno', Secondo: 'due', Terzo: 'tre'
Numero di parametri: 2
Parametri: due tre
Comando: ./parametri_bis.sh
Primo: 'due', Secondo: 'tre', Terzo: ”

Dopo l'esecuzione del comando “shift” i parametri si riducono da tre a due e i valori vengono spostati dalla variabile $2 alla $1 e dalla $3 alla $2; il valore della variabile $3, in seguito allo shift dei parametri, risulta nullo. Vengono modificati di conseguenza anche i valori delle variabili $@ e $#.

Come vedremo in seguito, il comando “shift” e le variabili $1, $2, $3, ..., vengono utilizzati anche per l'elaborazione dei parametri passati ad una funzione definita in uno script.

Home page

Indice

Introduzione

La shell Bash

Comandi interni, esterni e composti

Variabili, varibili d'ambiente e variabili speciali

Strutture di controllo

Funzioni

Esempi

Sintesi dei comandi principali

Bibliografia

HOME

Valid HTML 4.01! Valid CSS!
Author: Marco Liverani - Last modified: Sunday November 20, 2011 - URI: http://www.aquilante.net/bash/cap3_variabili.shtml