Capitolo successivo Capitolo precedente Indice

24. Bash - parametri, variabili, espansione e sostituzione

Un compito molto importante delle shell Unix è quello di rimpiazzare variabili e simboli speciali con quello che rappresentano. A fianco di questo problema, si pone la necessità di proteggere ciò che si vuole evitare sia espanso dalla shell. Anche questi simboli che proteggono contro l'espansione sono soggetti a loro volta ad un procedimento di sostituzione: quando la shell ha terminato l'interpretazione di una istruzione, questi devono essere rimossi in modo da non lasciare tracce per un eventuale programma che dovesse ricevere questi dati in forma di argomenti.

24.1 Quoting

In questo documento, il termine quoting viene usato per rendere l'idea del verbo quote inglese. Si vuole fare riferimento all'azione di racchiudere parti di testo all'interno di delimitatori (virgolette o apici) per evitare confusione nei comandi, o per poter utilizzare un simbolo che altrimenti avrebbe un significato speciale. Da un punto di vista umano, è l'equivalente della citazione.

Il metodo del quoting viene quindi usato per togliere il significato speciale che può avere un carattere o una parola per la shell. Ci sono tre meccanismi di quoting: il carattere di escape (rappresentato dalla barra rovescia), gli apici semplici e gli apici doppi (o virgolette).

Escape e continuazione

La barra inclinata rovescia (\) rappresenta il carattere di escape. Serve per preservare il carattere seguente, cioè evitare che venga interpretato diversamente da quello che è veramente.

Un caso particolare si ha quando il simbolo \ è esattamente l'ultimo carattere della riga, o meglio, quando questo è immediatamente seguito dal carattere newline (<LF>): rappresenta una continuazione nella riga successiva.

In un certo senso si potrebbe dire che si tratti anche in questo caso di un carattere di escape: toglie il significato normale che si da al carattere <LF>.

<!>   Il simbolo \ utilizzato per interrompere la riga e riprenderla nella riga successiva, può essere utilizzato sia con una shell interattiva che all'interno di uno script. Bisogna fare bene attenzione a non lasciare spazi dopo questo simbolo, altrimenti non si comporta più come segno di continuazione.

Esempi

$ ls -l \*

Tenta di visualizzare le caratteristiche del file il cui nome corrisponde a un asterisco (*). Se non venisse usata la barra rovescia che funge da escape, l'asterisco verrebbe trasformato nell'elenco dei nomi dei file contenuti nella directory corrente.

---------

#!/bin/bash
echo "Nella vecchia fattoria \
ia ia o"

Nello script precedente, il comando echo è stato spezzato a metà in modo da poter proseguire nella riga successiva.

Apici singoli

Racchiudendo una serie di caratteri tra una coppia di apici semplici (') si mantiene il valore letterale di questi caratteri. Evidentemente, un apice singolo non può essere contenuto in una stringa del genere.

<!>   L'apice inclinato nel modo opposto (`) viene usato con un altro significato che non rientra nel quoting.

Esempi

$ echo 'Questa stringa non viene trasformata: $0 $1 \ ... restano "inalterati".' [Invio]

Questa stringa non viene trasformata: $0 $1 \ ... restano "inalterati".

Apici doppi

Racchiudendo una serie di caratteri tra una coppia di doppi apici si mantiene il valore letterale di questi caratteri, a eccezione di $, ` e \. I simboli $ e ` mantengono il loro significato speciale all'interno di una stringa racchiusa tra doppi apici, mentre la barra obliqua rovescia, \, si comporta come carattere di escape solo quando è seguita da $, `, " e \; quando si trova al termine della riga serve come indicatore di continuazione nella riga successiva.

Si tratta di una particolarità molto importante, attraverso la quale è possibile definire delle stringhe in cui si possono inserire:

Esempi

$ echo "Il parametro \$0 contiene: \"$0\"" [Invio]

Il parametro $0 contiene: "-bash"

24.2 Parametri e variabili

Nella documentazione originale di bash si utilizza il termine ``parametro'' per identificare diversi tipi di entità:

In questo documento, per evitare confusioni, si riserva il termine ``parametro'' solo ai primi due tipi di entità.

L'elemento comune tra i parametri e le variabili è il modo con cui questi devono essere identificati quando si vuole leggere il loro contenuto: occorre il simbolo $ davanti al nome (o al simbolo) dell'entità in questione, mentre per assegnare un valore all'entità (sempre che ciò sia possibile), questo prefisso non deve essere indicato.

Parametri

Il termine parametro viene utilizzato qui per definire una variabile speciale che può essere solo letta e rappresenta alcuni elementi particolari dell'attività della shell. Come nel caso delle variabili, per fare riferimento al contenuto di un parametro occorre utilizzare il prefisso $ che di per se non è parte del nome del parametro. Tuttavia, per maggiore chiarezza espressiva, dal momento che non è possibile assegnare un valore a queste entità, quando nelle documentazioni si fa riferimento a un parametro, si utilizza quasi sempre il suo nome (o il simbolo che rappresenta) preceduto dal simbolo $ per evitare confusione.

Quindi, dire che il primo parametro posizionale si chiama $1 non è esatto: è semplicemente 1, solo che per leggerne il contenuto si deve aggiungere $ davanti e non esiste la possibilità di trattare questo 1 come una variabile di shell. Inoltre, parlare del parametro 1, può essere fonte di confusione.

Un parametro è definito, cioè esiste, quando contiene un valore, compresa la stringa vuota.

Parametri posizionali

Un parametro posizionale è definito da una o più cifre numeriche a eccezione del parametro $0 che ha invece un significato speciale. I parametri posizionali rappresentano gli argomenti forniti al comando: $1 è il primo, $2 è il secondo, e così di seguito.

Quando viene eseguita una funzione, questi parametri vengono temporaneamente rimpiazzati con gli argomenti forniti alla funzione.

Quando si utilizza un parametro composto da più di una cifra numerica, occorre racchiudere questo numero tra parentesi graffe: ${10}, ${11}, ...

Parametri speciali

I parametri speciali sono rappresentati da uno zero o un altro simbolo speciale.

$0

Restituisce il nome della shell o dello script. Se bash viene avviata con un file di comandi, $0 conterrà il nome di quel file. Se bash viene avviata con l'opzione -c, $0 conterrà il primo argomento dopo la stringa dei comandi (sempre che ce ne sia uno).

$*

Rappresenta l'insieme di tutti i parametri posizionali a partire dal primo. Quando viene utilizzato all'interno di doppi apici, rappresenta una singola parola composta dal contenuto dei parametri posizionali, spaziati dal primo carattere contenuto nella variabile speciale IFS. Se questa variabile non è definita oppure contiene una stringa vuota, viene utilizzato un singolo spazio. Per esempio, se IFS contenesse la sequenza xyz, "$*" sarebbe equivalente a "$1x$2x...".

<!>   La variabile di shell IFS contiene di solito la sequenza: <Spazio><Tab><newline>. Di conseguenza, viene normalmente utilizzato il carattere <Spazio> per staccare i vari parametri posizionali. Per cui, in pratica, la maggior parte delle volte, "$*" equivale a "$1 $2...".

$@

Rappresenta l'insieme di tutti i parametri posizionali a partire dal primo. Quando viene utilizzato all'interno di doppi apici, rappresenta una serie di parole, ognuna composta dal contenuto del rispettivo parametro posizionale. Di conseguenza, "$@" equivale a "$1" "$2".... Questo rappresenta una eccezione rispetto agli altri parametri: l'espansione di questo parametro genera diverse parole.

$#

Restituisce il numero della quantità di parametri posizionali.

$?

Restituisce lo stato dell'ultima pipeline eseguita in foreground (primo piano).

$-

Il trattino, restituisce i flag di opzione.

$$

Restituisce il PID della shell. Se viene utilizzato all'interno di una subshell, cioè tra parentesi tonde, restituisce il PID della shell principale e non quello della subshell.

$!

Restituisce il PID dell'ultimo comando avviato in background.

$_

Il simbolo di sottolineatura, restituisce l'ultimo argomento del comando precedente.

Variabili di shell

Una variabile è definita quando contiene un valore, compresa la stringa vuota. L'assegnazione di un valore si ottiene con una dichiarazione del tipo seguente.

<nome-di-variabile>=[<valore>]

Il nome di una variabile può contenere lettere, cifre numeriche e il segno di sottolineatura, ma il primo carattere non può essere un numero.

Se non viene fornito il valore da assegnare, si intende la stringa vuota. La lettura del contenuto di una variabile si ottiene facendone precedere il nome dal simbolo $.

---------

La tabella (seguente) mostra l'elenco delle variabili il cui valore viene assegnato direttamente dalla shell.


Variabile Descrizione
sh OPTIND L'indice del prossimo argomento da elaborare dal comando getopts.
OPTARG Il valore dell'ultimo argomento elaborato da getopts.
ksh RANDOM Un numero intero casuale.
REPLY La destinazione predefinita per il comando interno read.
SECONDS Il numero di secondi trascorsi dall'avvio della shell.
PWD La directory corrente.
OLDPWD La directory corrente precedentemente visitata.
LINENO Il numero della riga dello script o della funzione.
bash HISTCMD L'indice nel registro storico dei comandi.
UID Lo UID dell'utente.
EUID Lo UID efficace dell'utente.
GROUPS Un array contenente i numeri GID di cui l'utente è membro.
HOSTTYPE Il nome del tipo di computer.
OSTYPE Il nome del sistema operativo.
MACHTYPE Architettura e sistema operativo utilizzato.
BASH_VERSION Il numero di versione di bash.
BASH Il percorso completo della copia corrente di bash.
PPID Il PID del processo genitore della shell attuale.
SHLVL Il livello di annidamento di bash.
Elenco delle variabili di bash più importanti, il cui valore viene assegnato direttamente dalla shell stessa. L'elenco è classificato in base all'origine storica.

In particolare, è possibile assegnare un valore alla variabile SECONDS ottenendo il riavvio del conteggio dei secondi a partire da quel valore.

---------

La tabella (seguente) mostra l'elenco di alcune delle altre variabili utilizzate dalla shell.


Variabile Descrizione Predefinito
sh IFS Internal Field Separator. <SP><HT><LF>
PATH I percorsi di ricerca per i comandi.
HOME La directory home dell'utente.
CDPATH Il percorso di ricerca per il comando cd.
MAILPATH La directory dei file della posta.
PS1 Il prompt primario. ``bash\$ ''
PS2 Il prompt secondario. ``>''
csh IGNOREEOF Il numero di <EOF> necessari per terminare. 1
ksh PS3 Il prompt del comando select.
PS4 ``+ ''
TMOUT Tempo di attesa massima (secondi).
bash HISTSIZE Comandi da conservare dello storico. 500
HISTFILE File storico dei comandi inseriti. ``~/.bash_history''
ENV Il nome di un file di configurazione POSIX.
BASH_ENV File di configurazione per l'esecuzione di script.
Elenco di alcune delle altre variabili utilizzate da bash. L'elenco è classificato in base all'origine storica.

In particolare vanno prese in considerazioni le variabili descritte di seguito.

Esportazione

Quando si creano e/o si assegnano delle variabili, queste hanno una validità limitata all'ambito della shell stessa, per cui i comandi interni sono al corrente di queste variazioni, mentre i programmi che vengono avviati non ne risentono. Perché anche i programmi ricevano le variazioni fatte sulle variabili, queste devono essere ``esportate''. L'esportazione delle variabili si ottiene con il comando interno export.

Esempi

$ PIPPO="ciao"

$ export PIPPO

Crea la variabile PIPPO e quindi la esporta.

$ export PIPPO="ciao"

Esegue la stessa operazione dell'esempio precedente, ma in un colpo solo.

24.3 Espansione

Con questo termine si intende la traduzione di parametri e variabili e altre entità analoghe nel loro risultato finale. L'espansione, intesa in questi termini, viene eseguita sulla riga di comando, dopo che questa è stata scomposta in parole. Esistono sette tipi di espansione eseguiti nell'ordine seguente:

  1. parentesi graffe;
  2. tilde;
  3. parametri e variabili;
  4. comandi;
  5. aritmetica (da sinistra a destra);
  6. suddivisione delle parole;
  7. percorso o pathname.

Nei sistemi che possono gestirlo esiste un tipo addizionale. Si tratta della sostituzione di processo e qui non viene descritta.

Solo l'espansione attraverso parentesi graffe, la suddivisione in parole e l'espansione di pathname può cambiare il numero delle parole di una espressione. Gli altri tipi di espansione trasformano una parola in un'altra parola con l'unica eccezione del parametro $@.

Dopo tutte le fasi di espansione e sostituzione, tutti i simboli utilizzati per il quoting vengono eliminati.

Parola

Il termine ``parola'' ha un significato particolare nella terminologia utilizzata per la shell: si tratta di una sequenza di caratteri che rappresenta qualcosa di diverso da un operatore. In altri termini, si può definire come una stringa che viene presa così com'è e rappresenta una cosa sola. Per esempio, un argomento fornito a un programma è una parola.

L'operazione di `'suddivisione in parole'' riguarda il meccanismo con cui una stringa viene analizzata e suddivida in parole in base a un determinato criterio. Questo problema viene ripreso più avanti.

Espansione delle parentesi graffe

L'espansione delle parentesi graffe deriva da csh.

<prefisso>{<elenco>}<suffisso>

L'elenco indicato all'interno delle parentesi graffe è fatto di elementi separati da virgola. Il risultato è una serie di parole composte tutte dal prefisso e dal suffisso indicati e all'interno uno degli elementi della lista. Per esempio, a{b,c,d}e genera esattamente le tre parole abe ace ade.

Esempi

# mkdir /usr/local/src/bash/{old,new,dist,bugs}

Crea quattro directory: old, new, dist e bugs a partire da /usr/local/src/bash/.

# chown root /usr/{paperino/{qui,quo,qua},topolino/{t??.*,minnie}}

cambia proprietà di una serie di file:

/usr/paperino/qui
/usr/paperino/quo
/usr/paperino/qua
/usr/topolino/t??.*
/usr/topolino/minnie

e chiaramente, /usr/topolino/t??.* viene ulteriormente espanso.

Espansione della tilde

L'espansione della tilde deriva da csh.

Se una parola inizia con il simbolo tilde (~) si cerca di interpretare quello che segue, fino alla prima barra obliqua (/), come il nome di un utente (o nome di login), facendo in modo di sostituire questa prima parte con il nome della directory home dell'utente stesso. Se invece, dopo il carattere ~ c'è subito la barra, o nessun altro carattere, si intende il contenuto della variabile HOME, ovvero la directory home dell'utente attuale.

Esempi

$ cd ~

Corrisponde a uno spostamento nella directory home dell'utente.

$ cd ~pippo

Corrisponde a uno spostamento nella directory home dell'utente pippo.

<!>   Il carattere tilde viene usato anche per altri tipi di sostituzioni, e precisamente: la coppia ~+ viene sostituita con il contenuto di PWD, mentre, la coppia ~- viene sostituita con il contenuto di OLDPWD.

Espansione di parametri e variabili

Il modo normale in cui si fa riferimento a un parametro o una variabile è quello di anteporvi il simbolo dollaro ($), ma questo metodo può creare problemi all'intero delle stringhe, oppure quando si tratta di un parametro posizionale composto da più di una cifra decimale. La sintassi normale è quindi la seguente.

$<parametro> | ${<parametro>}

$<variabile> | ${<variabile>}

In uno di questi modi si ottiene quindi la sostituzione del parametro o della variabile con il suo contenuto.

Esempi

#!/bin/bash
echo " 1 arg. = $1"
echo " 2 arg. = $2"
echo " 3 arg. = $3"
...
echo "10 arg. = ${10}"
echo "11 arg. = ${11}"

Visualizza in sequenza l'elenco degli argomenti ricevuti, fino all'undicesimo.

---------

#!/bin/bash
UNO="Topo"
echo "${UNO}lino"

Compone la parola ``Topolino'' unendo il contenuto di una variabile con una terminazione costante.

I modi con cui con bash è possibile ottenere la sostituzione di parametri e variabili sono numerosi. Questi sono generalmente derivati da ksh e sono descritti nella sezione `Riferimenti particolari alle variabili'.

Sostituzione dei comandi

La sostituzione dei comandi consente di utilizzare quanto emesso attraverso lo standard output da un comando. Ci sono due forme possibili, la prima derivata da ksh e la seconda originaria di sh.

$(<comando>)

$`<comando>`

Nel secondo caso, la barra inclinata rovescia, eventualmente contenuta nella stringa, mantiene il suo significato letterale a eccezione di quando è seguita dai simboli $, ` o \.

<!>   Bisogna fare attenzione a non confondere gli apici usati per la sostituzione dei comandi con quelli usati per il quoting.

La sostituzione dei comandi può essere annidata. Per farlo, se si utilizza il vecchio metodo degli apici rovesci, occorre fare precedere a quelli più interni il simbolo di escape, ovvero la barra inclinata rovescia.

Se la sostituzione appare tra doppi apici, la suddivisione in parole e l'espansione di pathname non sono eseguite nel risultato.

Esempi

$ ELENCO=$(ls)

Crea e assegna alla variabile ELENCO l'elenco dei file della directory corrente.

$ ELENCO=$("ls a*")

Crea e assegna alla variabile ELENCO l'elenco dell'unico file a*, ammesso che esista.

$ rm $( find / -name "*.tmp" )

Elimina da tutto il filesystem i file che hanno l'estensione .tmp. Per farlo utilizza find che genera un elenco di tutti i nomi che soddisfano la condizione di ricerca.

Espansione di espressioni aritmetiche

Le espressioni aritmetiche consentono la valutazione delle espressioni stesse e l'espansione utilizzando il risultato. Esistono due forme per rappresentare la sostituzione tramite espressione aritmetica: nella prima l'espressione viene racchiusa tra parentesi quadre, nella seconda tra doppie parentesi tonde.

$[<espressione>]

$((<espressione>))

L'espressione viene trattata come se fosse racchiusa tra doppi apici, ma un doppio apice all'interno delle parentesi, non viene trattato in modo speciale. Tutti gli elementi all'interno della espressione sono sottoposti all'espansione di parametri, variabili, sostituzione di comandi ed eliminazione di simboli superflui per il quoting. La sostituzione aritmetica può essere annidata. Se l'espressione aritmetica non è valida, bash segnala l'errore e non esegue la sostituzione.

Esempi

$ echo "$((123+23))"

Emette il numero 146 corrispondente alla somma di 123 e 23.

$ VALORE=$[123+23]

Assegna alla variabile VALORE la somma di 123 e 23.

$ echo "$[123*$VALORE]"

Emette il prodotto di 123 per il valore contenuto nella variabile VALORE.

Suddivisione di parola

La shell esegue la ``suddivisione in parole'' dei risultati delle espansioni di parametri e variabili, della sostituzione di comandi e delle espansioni aritmetiche, che non siano avvenuti all'interno di un quoting di doppi apici.

La shell considera ogni carattere contenuto all'interno di IFS come un possibile delimitatore utile a determinare i punti in cui effettuare la separazione in parole.

Perché le cose funzionino così come si è abituati, è necessario che IFS contenga i valori predefiniti: <Spazio><Tab><newline> (ovvero <SP><HT><LF>). La variabile IFS è quindi importantissima: non può mancare o essere vuota.

Esempi

$ Pippo="b* d*" [Invio]

$ echo $Pippo [Invio]

In questo caso, avviene la suddivisione in parole del risultato della espansione della variabile Pippo. In pratica, è come se si facesse: echo b* d*. Il risultato è il seguente.

bin boot dev

---------

$ echo "$Pippo" [Invio]

In questo caso, la suddivisione in parole di quanto contenuto tra la coppia di doppi apici, non avviene e di conseguenza, non può avvenire la successiva espansione di pathname.

b* d*

---------

$ echo '$Pippo' [Invio]

Se si utilizzano gli apici semplici, non avviene alcuna sostituzione della variabile Pippo.

$Pippo

Espansione di pathname

Dopo la suddivisione in parole, bash scandisce ogni parola per la presenza dei simboli *, ? e [. Se incontra uno di questi caratteri, la parola che li contiene, viene trattata come modello e sostituita con un elenco ordinato alfabeticamente di pathname corrispondenti al modello. Se non si ottiene alcuna corrispondenza, il comportamento predefinito di bash è tale per cui la parola resta immutata, consentendo quindi l'utilizzo dei caratteri jolly per il globbing per identificare un pathname.

In generale, sarebbe meglio essere precisi quando si vuole indicare espressamente un nome che effettivamente contiene un asterisco o un punto interrogativo: si deve usare la barra inclinata rovescia che funge da carattere di escape.

Per convenzione, si considerano ``nascosti'' i file e le directory che iniziano con un punto. Di conseguenza, normalmente, i caratteri jolly non permettono di includere i nomi che iniziano con tale punto. Perciò, se necessario, questo punto deve essere indicato espressamente.

La barra inclinata di separazione dei pathname non viene mai generata automaticamente da un globbing.

Caratteri jolly

*

Corrisponde a qualsiasi stringa, compresa la stringa vuota.

?

Corrisponde a un qualsiasi carattere (uno solo).

[...]

Corrisponde a uno qualsiasi dei caratteri racchiusi tra parentesi quadre.

[a-z]

Corrisponde a uno qualsiasi dei caratteri compresi nell'intervallo da a a z.

[!...]

Corrisponde a tutti i caratteri esclusi quelli indicati.

[!a-z]

Corrisponde a tutti i caratteri esclusi quelli appartenenti all'intervallo indicato.

<!>   Per includere il trattino o la parentesi quadra chiusa in un raggruppamento tra parentesi quadre, occorre che questi simboli siano i primi o gli ultimi.

24.4 Eliminazione dei simboli di quoting rimanenti

Al termine dei vari processi di espansione, tutti i simboli usati per il quoting (\, ` e ") che non siano stati protetti attraverso l'uso della barra inclinata rovescia o di virgolette di qualche tipo, vengono rimossi.

 

1997.10.26 - Scritto da Daniele Giacomini   daniele@calion.com   (vedi copyright: Appunti Linux).


Capitolo successivo Capitolo precedente Indice