[successivo] [precedente] [inizio] [fine] [indice generale] [violazione GPL] [translators] [docinfo] [indice analitico] [volume] [parte]


Capitolo 61.   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 a un procedimento di sostituzione: quando la shell ha terminato l'interpretazione di un'istruzione, questi devono essere rimossi in modo da non lasciarne traccia per un eventuale programma che dovesse ricevere tali dati in forma di argomenti.

61.1   Quoting: protezione

Il quoting è un'azione con la quale si toglie il significato speciale che può avere qualcosa per la shell. Si distinguono tre possibilità: il carattere di escape (rappresentato dalla barra obliqua inversa), gli apici semplici e gli apici doppi (o virgolette). In generale, il concetto può essere trasferito in quello della protezione da un'interpretazione errata di ciò che si intende veramente.

È importante notare che il concetto di «protezione» è utilizzato in molte situazioni estranee all'uso della shell e ogni contesto può avere una logica differente.

61.1.1   Escape e continuazione

La barra obliqua inversa (\) rappresenta il carattere di escape. Serve per preservare il significato letterale del carattere successivo, 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 è seguito immediatamente dal codice di interruzione di riga: rappresenta una continuazione nella riga successiva.(1)

Il simbolo \, utilizzato per interrompere un'istruzione 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 obliqua inversa che funge da escape, l'asterisco verrebbe trasformato nell'elenco dei nomi dei file contenuti nella directory corrente.

#!/bin/bash
echo "Saluti e baci \
paga la multa e taci"

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

61.1.2   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 in quello della protezione delle stringhe delimitate.

Esempi

echo 'Attenzione: $0 $1 \ ... restano "inalterati".'[Invio]

Attenzione: $0 $1 \ ... restano "inalterati".

61.1.3   Apici doppi

Racchiudendo una serie di caratteri tra una coppia di apici doppi si mantiene il valore letterale di questi caratteri, a eccezione di $, ` e \. I simboli $ e ` (dollaro e apice inverso) mantengono il loro significato speciale all'interno di una stringa racchiusa tra apici doppi, mentre la barra obliqua inversa (\) 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"

61.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.

61.2.1   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 sé 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 $.

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 nulla.

61.2.1.1   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 rimpiazzati temporaneamente 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},...

61.2.1.2   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 la shell Bash viene avviata con un file di comandi, $0 conterrà il nome di quel file. Se la shell 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 apici doppi, rappresenta un'unica 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 nulla, viene utilizzato uno spazio singolo. Per esempio, se IFS contenesse la sequenza xyz, "$*" sarebbe equivalente a "$1x$2x...".

La variabile di shell IFS contiene di solito la sequenza: <SP><HT><LF> (corrispondente a uno spazio normale, un carattere di tabulazione e al codice di interruzione di riga nella maggior parte dei sistemi Unix). Di conseguenza, viene utilizzato normalmente il carattere spazio (<SP>) 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 apici doppi, rappresenta una serie di parole, ognuna composta dal contenuto del parametro posizionale rispettivo. Di conseguenza, "$@" equivale a "$1" "$2" ... "$n". Questo rappresenta un'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 primo piano (foreground).

$-

Il trattino, restituisce la serie di lettere corrispondenti alle modalità configurabili attraverso il comando interno set.

$$

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

$!

Restituisce il valore dell'errore.

$_

Il trattino basso, restituisce l'ultimo argomento del comando precedente.

61.2.2   Variabili di shell

Una variabile è definita quando contiene un valore, compresa la stringa nulla. L'assegnamento 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 trattino basso, ma il primo carattere non può essere un numero.

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

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

Tabella 61.1. Elenco delle variabili più importanti della shell Bash, il cui valore viene assegnato direttamente dalla shell stessa. L'elenco è classificato in base all'origine storica.

Origine 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 visitata precedentemente.
" LINENO Il numero della riga dello script o della funzione.
bash HISTCMD L'indice nel registro storico dei comandi.
" UID Il numero UID dell'utente.
" EUID Il numero UID efficace dell'utente.
" GROUPS Un array contenente i numeri GID di cui l'utente è membro.
" HOSTTYPE Il nome del tipo di elaboratore.
" OSTYPE Il nome del sistema operativo.
" MACHTYPE Architettura e sistema operativo utilizzato.
" BASH_VERSION Il numero di versione di Bash.
" BASH Il percorso assoluto della copia corrente dell'eseguibile bash.
" PPID Il numero PID del processo genitore della shell attuale.
" SHLVL Il livello di annidamento dell'eseguibile bash.

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

La tabella 61.2 mostra l'elenco di alcune delle altre variabili utilizzate dalla shell.

Tabella 61.2. Elenco di alcune delle altre variabili utilizzate dalla shell Bash. L'elenco è classificato in base all'origine storica.

Origine Variabile Descrizione Predefinito
sh IFS Internal field separator. <SP><HT><LF>
" PATH I percorsi di ricerca per i comandi.
" HOME La directory personale dell'utente.
" CDPATH Il percorso di ricerca per il comando cd.
" MAILPATH La directory dei file della posta elettronica.
" PS1 L'invito primario. «bash$ »
" PS2 L'invito secondario. «>»
csh IGNOREEOF Il numero di EOF necessari per terminare. 1
ksh PS3 l'invito del comando select.
" PS4 «+ »
" TMOUT Tempo di attesa massima (secondi).
bash HISTSIZE Comandi da conservare nello 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.

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

PATH

È un elenco di directory separato da due punti verticali (:). Il valore predefinito dipende dalla configurazione della shell e un valore comune potrebbe essere il seguente:

/usr/local/bin:/bin:/usr/bin:.:

È importante notare, soprattutto per chi è abituato a utilizzare il sistema operativo Dos, che se si vuole includere nel percorso di ricerca la directory corrente, questa deve essere indicata tra i percorsi possibili. Nell'esempio è stata messa alla fine, come si fa di solito per motivi di sicurezza.

PS1

L'invito primario. Un esempio possibile è \u@\h:\w\$  che in particolare mostra anche il nome dell'utente come nell'esempio seguente:

daniele@dinkel:~$

Se la stringa si indica delimitandola tra apici doppi, si deve di fatto trasformare in "\\u@\\h:\\w\\$ ".

61.2.3   Esportazione delle variabili

Quando si creano 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.

61.3   Espansione

Con questo termine si intende la traduzione di parametri, 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 percorso, possono cambiare il numero delle parole di un'espressione. Gli altri tipi di espansione trasformano una parola in un'altra parola con l'unica eccezione del parametro $@ che invece si espande in più parole.

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

61.3.1   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 criterio determinato. Questo problema viene ripreso più avanti.

61.3.2   Espansione delle parentesi graffe

L'espansione delle parentesi graffe deriva dalla shell C.

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, contenenti 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/pippo/{vecchio,nuovo,dist,bachi}

Crea quattro directory: vecchio/, nuovo/, dist/ e bachi/ a partire da /usr/local/src/pippo/.

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 espanso ulteriormente.

61.3.3   Espansione della tilde

L'espansione della tilde deriva dalla shell C.

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

Esempi

cd ~

Corrisponde a uno spostamento nella directory personale dell'utente.

cd ~tizio

Corrisponde a uno spostamento nella directory personale dell'utente tizio (ammesso che i permessi lo consentano).

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

61.3.4   Espansione di parametri e variabili

Il modo normale in cui si fa riferimento a un parametro o a una variabile è quello di anteporvi il simbolo dollaro ($), ma questo metodo può creare problemi all'interno 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="Dani"
echo "${UNO}ele"

Compone la parola Daniele unendo il contenuto di una variabile con una terminazione costante.

Con la shell Bash, i modi in cui è possibile ottenere la sostituzione di parametri e variabili sono numerosi. Questi sono derivati generalmente dalla shell Korn e sono descritti nella sezione 63.4.

61.3.5   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 dalla shell Korn e la seconda originaria della shell Bourne.

$(comando)

`comando`

Nel secondo caso dove si utilizzano gli apici inversi, la barra obliqua inversa (\), che fosse contenuta eventualmente 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 la protezione delle stringhe.

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

Se la sostituzione appare tra apici doppi, la suddivisione in parole e l'espansione di percorso 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 file system 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.

61.3.6   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 apici doppi, ma un apice doppio all'interno delle parentesi non viene interpretato in modo speciale. Tutti gli elementi all'interno dell'espressione sono sottoposti all'espansione di parametri, variabili, sostituzione di comandi ed eliminazione di simboli superflui per la protezione. La sostituzione aritmetica può essere annidata. Se l'espressione aritmetica non è valida, si ottiene una segnalazione di errore senza alcuna 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.

61.3.7   Suddivisione di parole

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 stringhe protette attraverso la delimitazione con apici doppi.

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 dell'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 non avviene la suddivisione in parole di quanto contenuto tra la coppia di apici doppi e di conseguenza non può avvenire la successiva espansione di percorso.

b* d*

/$ echo '$Pippo'[Invio]

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

$Pippo

61.3.8   Espansione di percorso

Dopo la suddivisione in parole, la shell 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 percorsi corrispondenti al modello. Se non si ottiene alcuna corrispondenza, il comportamento predefinito è tale per cui la parola resta immutata, consentendo quindi l'utilizzo dei caratteri jolly per il globbing (i metacaratteri) per identificare un percorso.(2)

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

La barra obliqua di separazione dei percorsi non viene mai generata automaticamente dall'espansione di percorso (il globbing).

Caratteri jolly, o metacaratteri

*

Corrisponde a qualsiasi stringa, compresa la stringa nulla.

?

Corrisponde a un carattere qualsiasi (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.

61.4   Eliminazione dei simboli di protezione rimanenti

Al termine dei vari processi di espansione, tutti i simboli usati per la protezione (\, ` e ") che a loro volta non siano stati protetti attraverso l'uso della barra obliqua inversa o di virgolette di qualche tipo, vengono rimossi.

Appunti di informatica libera 2003.01.01 --- Copyright © 2000-2003 Daniele Giacomini -- daniele @ swlibero.org

1) 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 dà al codice di interruzione di riga.

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


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome bash_parametri_variabili_espansione_e_sostituzione.html

[successivo] [precedente] [inizio] [fine] [indice generale] [violazione GPL] [translators] [docinfo] [indice analitico]