[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 139.   Shell POSIX: utilizzo generale

La shell POSIX è in pratica la shell Bourne standardizzata. Non esiste una sola shell POSIX, ma tante interpretazioni diverse, più o meno derivate da quella di Bourne.

Il primo elemento comune di queste shell è il programma eseguibile che le rappresenta: /bin/sh. In pratica, si tratta normalmente di un collegamento simbolico alla shell effettiva che ricopre quel ruolo. In particolare, ci sono shell come Bash che si adeguano agli standard quando sono avviate con quel nome.

139.1   Avvio e conclusione

L'eseguibile della shell POSIX è sh, collocato nella directory /bin/:

sh [opzioni] [file_script] [argomenti]

Si distinguono fondamentalmente due tipi di modalità di funzionamento: interattiva e non interattiva. Quando l'eseguibile sh viene avviato con l'indicazione del nome di un file, questo tenta di eseguirlo come uno script (in tal caso non conta che il file abbia i permessi di esecuzione e nemmeno che contenga la dichiarazione iniziale #!/bin/sh). Gli eventuali argomenti che possono seguire il nome del file, vengono passati allo script in forma di parametri (come viene descritto più avanti).

La shell è interattiva quando interagisce con l'utente e di conseguenza mostra un invito a inserire dei comandi. L'eseguibile sh può essere avviato eventualmente in modo esplicitamente interattivo utilizzando l'opzione -i.

Quando la shell funziona in modo interattivo, la variabile di ambiente PS1 determina l'aspetto dell'invito, mentre il parametro $- contiene anche la lettera i (i concetti relativi a variabili e parametri vengono chiariti in seguito).

Una shell interattiva può a sua volta essere una «shell di login» o meno. La distinzione serve alla shell per determinare quali file di configurazione utilizzare. Una shell di login è quella in cui il parametro zero, contiene un trattino (-) come primo carattere (di solito contiene esattamente il valore -sh). In pratica è, o dovrebbe essere, quello che si ha di fronte quando è stata completata la procedura di accesso.

echo $0[Invio]

-sh

Secondo lo standard POSIX, la shell di login esegue il contenuto del file indicato nella variabile di ambiente ENV; tuttavia, di solito queste shell si comportano come la shell Bourne, per cui eseguono il contenuto dei file /etc/profile e ~/.profile in sequenza. La shell POSIX interattiva esegue inizialmente il contenuto del file indicato nella variabile di ambiente ENV.

Una shell non interattiva conclude il suo funzionamento al termine dello script che interpreta. Una shell interattiva termina di funzionare quando le si impartisce il comando exit.

139.2   Interpretazione dei comandi: parametri, variabili, espansione e sostituzione

Una volta avviata la shell in modo interattivo, questa mostra l'invito a inserire dei comandi, che prima di essere eseguiti sono soggetti a un'interpretazione da parte della shell stessa. Nello stesso modo viene interpretato un comando contenuto all'interno di uno script che la shell esegue.

Il meccanismo di interpretazione della shell è molto complesso, perché prevede molte situazioni differenti, in cui ciò che appare deve essere sostituito da qualcosa di diverso. Si può pensare inizialmente a questo meccanismo come a qualcosa che assomiglia alle variabili di un linguaggio di programmazione comune; tuttavia la realtà di una shell è molto più varia e difficile, tanto che comprenderne bene il funzionamento richiede anche più impegno rispetto a un linguaggio di programmazione comune.

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

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 (salvo quando il contesto associa a una sequenza \x determinata un significato speciale).

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.

Il simbolo \, utilizzato per interrompere un'istruzione e riprenderla nella riga successiva, può essere utilizzato sia con una shell interattiva, sia all'interno di uno script. In ogni caso, bisogna fare bene attenzione a non lasciare spazi dopo questo simbolo, altrimenti non si comporterebbe più come segno di continuazione, ma come protezione di un carattere spazio.

L'esempio seguente mostra l'uso del comando echo per visualizzare un asterisco, ma dal momento che questo verrebbe rimpiazzato dall'elenco dei file e delle directory presenti nella directory corrente, viene protetto con la barra obliqua inversa:

echo \*[Invio]

*

L'esempio successivo rappresenta uno script, in cui il comando echo viene usato per visualizzare una stringa che nello script viene divisa su due righe, per comodità:

#!/bin/sh
echo "Saluti e baci \
bla bla bla."

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.

Si tenga presente che l'apice inclinato nel modo opposto (`) viene usato con un altro significato che non rientra in quello della protezione delle stringhe delimitate.

L'esempio seguente mostra l'uso del comando echo per visualizzare una frase, contenente simboli che in condizioni normali verrebbero rimpiazzati da altre cose:

echo 'Attenzione: *, \, e altri oggetti particolari \
  \restano "inalterati".'
[Invio]

Attenzione: *, \, e altri oggetti particolari restano "inalterati".

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 (di protezione) solo quando è seguita da $, `, " e \; inoltre, 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: variabili, parametri e comandi da sostituire.

L'esempio seguente mostra l'uso del comando echo per mostrare una frase in cui si fa riferimento al parametro posizionale zero (che viene descritto in seguito). Questo parametro viene prima indicato proteggendo il dollaro, in modo da impedire che venga interpretato come tale, quindi viene inserito in modo da ottenerne il contenuto:

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

Il parametro $0 contiene: "-sh"

139.2.2   Parametri e variabili

Nella documentazione comune si utilizza il termine «parametro» per identificare diversi tipi di entità: parametri posizionali; parametri speciali; variabili di shell. 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 oggetti 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. Per la precisione, per leggere il contenuto di un parametro o di una variabile si usa normalmente una delle due forme seguenti:

$nome
${nome}

In pratica si usano le parentesi graffe per circoscrivere il nome o il simbolo associato alla variabile o al parametro, quando c'è la necessità di evitare ambiguità di qualche tipo.

I parametri sono delle variabili speciali che possono essere solo lette e rappresentano alcuni elementi particolari dell'attività della shell. Un parametro è definito, cioè esiste, quando contiene un valore, compresa la stringa nulla.

Parametro Descrizione
n
Un parametro posizionale è definito da una o più cifre numeriche a eccezione dello zero 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 si utilizza un parametro composto da più di una cifra numerica, è indispensabile racchiuderlo tra parentesi graffe; per esempio: ${10}, ${11},...
0
Restituisce il nome della shell o dello script. Se la shell viene avviata con un file di comandi, $0 si espande nel nome di quel file. Se la shell viene avviata con l'opzione -c, $0 si espande nel primo argomento dopo la stringa dei comandi (sempre che ce ne sia uno).
*
L'asterisco 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, 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 comportamento rappresenta un'eccezione rispetto agli altri parametri che invece si limitano a generare una sola parola.
#
Restituisce il numero di parametri posizionali esistenti.
?
Restituisce il valore restituito dall'ultimo condotto eseguito in primo piano (foreground). In pratica, restituisce il valore dell'ultimo comando eseguito.
-
Il trattino, restituisce la serie di lettere corrispondenti alle modalità configurabili attraverso il comando interno set o con opzioni particolari della riga di comando.
$
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 numero PID del processo avviato più di recente e messo sullo sfondo.

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. Come già accennato, la lettura del contenuto di una variabile si ottiene facendone precedere il nome dal simbolo $.

Tabella 139.7. Elenco delle variabili più importanti di una shell POSIX.

Variabile Descrizione
PWD
La directory corrente. Il contenuto della variabile viene modificato dal comando cd.
OLDPWD
La directory corrente visitata precedentemente. Il contenuto della variabile viene modificato dal comando cd.
PPID
Il numero PID del processo genitore della shell attuale.
IFS
Internal field separator. Il contenuto predefinito della variabile dovrebbe essere: <SP><HT><LF>.
PATH
I percorsi di ricerca per i comandi, separati dal carattere :.
HOME
La directory personale dell'utente.
CDPATH
Il percorso di ricerca per il comando cd (di solito la variabile contiene la stringa nulla).
MAIL
Il percorso del file che rappresenta la cartella di posta in entrata dell'utente.
MAILCHECK
La frequenza, in secondi, con cui si deve verificare la presenza di messaggi nuovi nella cartella corrispondente alla variabile MAIL. Se MAILCHECK è vuota o contiene il valore zero, il controllo avviene ogni volta che deve essere emesso un nuovo invito.
MAILPATH
Questa variabile, se definita, prende il sopravvento su MAIL e definisce un elenco di percorsi per altrettante cartelle di posta elettronica alternative. L'elenco è separato con il carattere :.
OPTIND
Contiene l'indice del prossimo argomento da elaborare dal comando getopts.
OPTARG
Il valore dell'ultimo argomento elaborato da getopts.
PS1
L'invito primario. Di solito, il valore predefinito di questa variabile fa sì che sia rappresentato un dollaro o un cancelletto a seconda che si tratti di un utente comune o dell'utente root.
PS2
L'invito secondario, che appare quando si deve completare un comando. Il valore predefinito è normalmente .
ENV
Il nome di un file di configurazione per una shell POSIX.

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. L'esempio seguente mostra la creazione della variabile PIPPO, a cui viene assegnato un valore, quindi si vede anche la sua esportazione per gli altri programmi:

PIPPO="ciao"[Invio]

export PIPPO[Invio]

139.2.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 almeno sei tipi di espansione eseguiti nell'ordine seguente:

  1. tilde;

  2. parametri e variabili;

  3. comandi;

  4. aritmetica (da sinistra a destra);

  5. suddivisione delle parole;

  6. percorso o pathname.

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

Al termine dei vari processi di espansione e sostituzione, 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.

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 in una sezione apposita.

139.2.4   Espansione della tilde

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. Segue la descrizione di due esempi:

139.2.5   Espansione di parametri e variabili

Come già accennato in precedenza, il modo normale con 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. Si osservino gli esempi seguenti. Il primo di questi visualizza in sequenza l'elenco degli argomenti ricevuti, fino all'undicesimo:

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

L'esempio seguente, invece, compone il nome Daniele unendo il contenuto di una variabile con una terminazione costante:

#!/bin/sh
UNO="Dani"
echo "${UNO}ele"

Oltre a questi modi «normali», è possibile espandere un parametro o una variabile indicando valori predefiniti; inoltre è possibile eseguire qualche operazione sulle stringhe, ma questi modelli di espansione non vengono descritti.

139.2.6   Sostituzione dei comandi

La sostituzione dei comandi consente di utilizzare quanto emesso attraverso lo standard output da un comando. Ci sono due forme possibili:

$(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 è inserita in una stringa delimitata tra apici doppi, la suddivisione in parole e l'espansione di percorso non sono eseguite nel risultato.

Segue la descrizione di alcuni esempi:

139.2.7   Espansione di espressioni aritmetiche

Le espressioni aritmetiche consentono la valutazione delle espressioni stesse e l'espansione utilizzando il risultato:

$((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.

Segue la descrizione di alcuni esempi:

139.2.8   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 e non può essere vuota.

Segue la descrizione di alcuni esempi.

139.2.9   Espansione di percorso

Dopo la suddivisione in parole, la shell 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 comune è tale per cui la parola resta immutata, consentendo quindi l'utilizzo dei caratteri jolly per il globbing (i metacaratteri) per identificare un percorso.

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.

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

Tabella 139.17. Modelli utilizzabili per ottenere un'espansione di percorso.

Modello Descrizione
*
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.
[!...]
Corrisponde a tutti i caratteri esclusi quelli indicati.
[a-z]
Corrisponde a uno qualsiasi dei caratteri compresi nell'intervallo da a a z.
[!a-z]
Corrisponde a tutti i caratteri esclusi quelli appartenenti all'intervallo indicato.

139.3   Comandi

Con il termine «comando» si intendono diversi tipi di entità che hanno in comune il modo con cui vengono utilizzate: attraverso un nome seguito eventualmente da alcuni argomenti. Può trattarsi dei casi seguenti.

139.3.1   Valore restituito dai comandi: «exit status»

Un comando che termina la sua esecuzione restituisce un valore, così come fanno le funzioni nei linguaggi di programmazione. Un comando, che quindi può essere un comando interno, una funzione di shell o un programma, può restituire solo un valore numerico. Di solito, si considera un valore di uscita pari a zero come indice di una conclusione regolare del comando, cioè senza errori di alcun genere.

Dal momento che può essere restituito solo un valore numerico, quando il risultato di un'esecuzione di un comando viene utilizzato in un'espressione logica (booleana), si considera lo zero come equivalente a Vero, mentre un qualunque altro valore viene considerato equivalente a Falso.

Per conto suo, la shell restituisce il valore di uscita dell'ultimo comando eseguito, se non riscontra un errore di sintassi, nel qual caso genera un valore diverso da zero (Falso).

139.3.2   Condotto

Il condotto (pipeline) è una sequenza di uno o più comandi separati da una barra verticale (|). Il formato normale per un condotto è il seguente:

[!] comando1 [ | comando2...]

Lo standard output del primo comando è incanalato nello standard input del secondo comando. Questa connessione è effettuata prima di qualsiasi ridirezione specificata dal comando. Come si vede dalla sintassi, per poter parlare di condotto basta anche un solo comando.

Normalmente, il valore restituito dal condotto corrisponde a quello dell'ultimo comando che viene eseguito all'interno di questo.

Se all'inizio del condotto viene posto un punto esclamativo (!), il valore restituito corrisponde alla negazione logica del risultato normale.

Si osservi che il punto esclamativo deve essere separato dal comando che inizia il condotto, altrimenti potrebbe essere interpretato come parte del nome del comando, oppure, come avviene con la shell Bash, potrebbe servire per richiamare un comando dallo storico degli ultimi comandi inseriti.

La shell attende che tutti i comandi del condotto siano terminati prima di restituire un valore.

Ogni comando in un condotto è eseguito come un processo separato.

139.3.3   Lista di comandi

La lista di comandi è una sequenza di uno o più condotti separati da ;, &, && o ||, terminata da ;, & o dal codice di interruzione di riga. Parti della lista sono raggruppabili attraverso parentesi (tonde o graffe) per controllarne la sequenza di esecuzione. Il valore di uscita della lista corrisponde a quello dell'ultimo comando della stessa lista che è stato possibile eseguire.

I comandi separati da un punto e virgola (;) sono eseguiti sequenzialmente. Il simbolo punto e virgola può essere utilizzato per separare una serie di comandi posti sulla stessa riga, o per terminare una lista di comandi quando c'è la necessità di farlo (per distinguerlo dall'inizio di qualcos'altro). Idealmente, il punto e virgola sostituisce il codice di interruzione di riga.

L'esempio seguente avvia in sequenza una serie di comandi per la compilazione e installazione di un programma ipotetico:

./configure ; make ; make install[Invio]

L'operatore di controllo && si comporta come l'operatore booleano AND: se il valore di uscita di ciò che sta alla sinistra è zero (Vero), viene eseguito anche quanto sta alla destra. Dal punto di vista pratico, viene eseguito il secondo comando solo se il primo ha terminato il suo compito con successo.

Nell'esempio seguente viene eseguito il comando mkdir ./prova. Se ha successo viene eseguito il comando successivo che visualizza un messaggio di conferma:

mkdir ./prova && echo "Creata la directory prova"[Invio]

L'operatore di controllo || si comporta come l'operatore booleano OR: se il valore di uscita di ciò che sta alla sinistra è zero (Vero), il comando alla destra non viene eseguito. Dal punto di vista pratico, viene eseguito il secondo comando solo se il primo non ha potuto essere eseguito, oppure se ha terminato il suo compito riportando un qualche tipo di insuccesso.

Nell'esempio seguente si tenta di creare la directory prova/, se il comando fallisce si tenta di creare prova1/ al suo posto:

mkdir ./prova || mkdir ./prova1[Invio]

139.3.4   Avvio sullo sfondo con «&»

I comandi seguiti dal simbolo & vengono messi in esecuzione sullo sfondo. La descrizione del meccanismo con cui i programmi possono essere messi e gestiti sullo sfondo viene fatta nella sezione 139.5. Dal momento che non si attende la loro conclusione per passare all'esecuzione di quelli successivi, il valore restituito è sempre zero. Segue la descrizione di alcuni esempi.

Le liste, o parti di esse, possono essere racchiuse utilizzando delle parentesi tonde. Questo tipo di lista viene eseguita in una subshell. Gli assegnamenti di variabili e l'esecuzione di comandi interni che influenzano l'ambiente della shell non lasciano effetti dopo che il comando composto è completato. Il valore restituito è quello dell'ultimo comando eseguito all'interno delle parentesi.

L'esempio seguente crea la directory prova/ o prova1/. Se ci riesce, visualizza il messaggio.

(mkdir ./prova || mkdir ./prova1) && echo "Creata la directory"[Invio]

Si osservi che il contenuto delle parentesi tonde può essere a contatto delle parentesi stesse, così come si vede nell'esempio.

Le liste possono essere raggruppate utilizzando delle parentesi graffe. Queste vengono eseguite nell'ambiente di shell corrente. Si tratta quindi di un semplice raggruppamento di liste su più righe. Il valore restituito è quello dell'ultimo comando eseguito all'interno delle parentesi.

L'uso delle parentesi graffe è indicato particolarmente nella preparazione di script di shell. Gli esempi seguenti sono equivalenti.

#!/bin/sh
{  mkdir ./prova ; cd ./prova ; ls ; }
#!/bin/sh
{  mkdir ./prova
   cd ./prova
   ls
}

Si osservi che quanto contenuto tra parentesi graffe, così come si vede negli esempi, non può essere aderente alle parentesi stesse; inoltre è indispensabile che dopo l'ultimo comando si dia il punto e virgola, oppure che la parentesi di chiusura appaia dopo un codice di interruzione di riga.

139.3.5   Alias

Attraverso i comandi interni alias e unalias è possibile definire ed eliminare degli alias, ovvero dei sostituti ai comandi. Prima di eseguire un comando di qualunque tipo, la shell cerca la prima parola di questo comando (quello che lo identifica) all'interno dell'elenco degli alias; se la trova lì, la sostituisce con il suo alias. La sostituzione non avviene se il comando o la prima parola di questo è delimitata tra virgolette. Il nome dell'alias non può contenere il simbolo =. La trasformazione in base alla presenza di un alias continua anche per la prima parola del testo di rimpiazzo della prima sostituzione. Quindi, un alias può fare riferimento a un altro alias e così di seguito. Questo ciclo si ferma quando non ci sono più corrispondenze con nuovi alias in modo da evitare una ricorsione infinita.

Gli alias non vengono espansi quando la shell non funziona in modalità interattiva; di conseguenza, non sono disponibili durante l'esecuzione di uno script.

In generale, l'utilizzo di alias è superato dall'uso delle funzioni, se queste sono disponibili con la shell che si ha a disposizione.

L'uso di alias può essere utile se questi vengono definiti automaticamente per ogni avvio della shell, per esempio inserendoli all'interno di /etc/profile.

Segue la descrizione di alcuni esempi.

139.4   Ridirezione

Prima che un comando sia eseguito, si possono ridirigere i suoi flussi di dati in ingresso e in uscita, utilizzando una notazione speciale che viene interpretata dalla shell. La ridirezione viene eseguita, nell'ordine in cui appare, a partire da sinistra verso destra.

Se si utilizza il simbolo < da solo, la ridirezione si riferisce allo standard input (corrispondente al descrittore di file zero. Se si utilizza il simbolo > da solo, la ridirezione si riferisce allo standard output (corrispondente al descrittore di file numero uno). La parola che segue l'operatore di ridirezione è sottoposta a tutta la serie di espansioni e sostituzioni possibili. Se questa parola si espande in più parole dovrebbe essere segnalato un errore.

Si distinguono normalmente tre tipi standard di descrittori di file per l'input e l'output:

Tabella 139.20. Sintassi per la ridirezione.

Sintassi Descrizione
[n]< file
La ridirezione dell'input fa sì che il file il cui nome risulta dall'espansione della parola alla destra del simbolo < venga letto e inviato al descrittore di file n, oppure, se non indicato, allo standard input pari al descrittore di file zero.
[n]> file
La ridirezione dell'output fa sì che il file il cui nome risulta dall'espansione della parola alla destra del simbolo > venga aperto in scrittura per ricevere quanto proveniente dal descrittore di file n, oppure, se non indicato, dallo standard output pari al descrittore di file numero uno.
Di solito, se il file da aprire in scrittura esiste già, viene sovrascritto.
[n]>> file
La ridirezione dell'output fatta in questo modo fa sì che se il file da aprire in scrittura esiste già, questo non sia sovrascritto, ma gli siano semplicemente aggiunti i dati.
<<[-] parola_di_delimitazione
    testo
    ...
parola_di_delimitazione
Si tratta di un tipo di ridirezione particolare e poco usato. Istruisce la shell di leggere le righe successive fino a quando viene incontrata la parola indicata (senza spazi iniziali); successivamente invia quanto accumulato in questo modo allo standard input del comando indicato. In pratica, la parola indica la fine della fase di lettura. Non è possibile fare giungere l'input da una fonte diversa.
Se la parola viene racchiusa tra virgolette, quelle usate per la protezione delle stringhe, si intende che il testo contenuto non deve essere espanso. Altrimenti, il testo viene espanso come di consueto.
Se si usa il trattino (<<-), significa che le tabulazioni iniziali nel testo vengono eliminate.
[n]<&m
In questo modo si unisce il descrittore m al descrittore n di ingresso oppure, in mancanza dell'indicazione di n, si unisce allo standard input.
[n]<&-
Chiude il descrittore n oppure, in mancanza dell'indicazione di n, chiude lo standard input.
[n]>&m
In questo modo si unisce il descrittore n al descrittore m di uscita oppure, in mancanza dell'indicazione di n, si unisce lo standard output.
[n]>&-
Chiude il descrittore n oppure, in mancanza dell'indicazione di n, chiude lo standard output.
[n]<> file
In questo modo si apre il file indicato in lettura e scrittura, collegando i due flussi al descrittore n. Se questo descrittore non è specificato si intende l'utilizzo di entrambi standard input e standard output.

La tabella successiva riduce i modelli alle situazioni più comuni.

Sintassi Descrizione
< file
Invia il contenuto del file allo standard input.
> file
Crea o sovrascrive il file con quanto ottenuto dallo standard output.
2> file
Crea o sovrascrive il file con quanto ottenuto dallo standard error.
>> file
Crea o estende il file con quanto ottenuto dallo standard output.
2>> file
Crea o estende il file con quanto ottenuto dallo standard error.
2>&1
Invia lo standard error nello standard output.

Segue la descrizione di alcuni esempi.

139.4.1   Ridirezione e script

Lo standard input di uno script è diretto al primo comando a essere eseguito che sia in grado di riceverlo. Lo standard output e lo standard error di uno script provengono dai comandi che emettono qualcosa attraverso quei canali.

Mentre il fatto che l'output derivi dai comandi contenuti nello script dovrebbe essere intuitivo, il modo con cui è possibile ricevere l'input potrebbe non esserlo altrettanto. Il problema di creare uno script che sia in grado di ricevere dati dallo standard input si pone in particolare quando si deve realizzare il classico filtro di input per un file /etc/printcap. Nell'esempio seguente, il filtro di input riceve dati dallo standard input attraverso cat; quindi, con un condotto si arriva a un testo stampabile che viene inviato alla stampante predefinita (esistono molte interpretazioni differenti del programma unix2dos; in questo caso si considera che si tratti di un filtro che elabora ciò che gli viene passato attraverso lo standard input, restituendo il risultato dallo standard output).

#!/bin/sh
# /var/spool/text/input-filter
cat | /usr/bin/unix2dos | lpr

Un'altra cosa interessante in uno script è l'uso delle parentesi graffe per raggruppare un insieme di istruzioni che devono generare un flusso di dati comune da inviare a un solo comando:

#!/bin/sh
{ ls -l / ; ls /bin } | sort

In questo caso, vengono eseguiti i due comandi ls e quanto emesso da questi attraverso lo standard output viene inviato complessivamente a sort.

139.5   Controllo dei job

Il controllo dei job si riferisce alla possibilità di sospendere e ripristinare selettivamente l'esecuzione dei processi. La shell associa un job a ogni condotto. Mantiene una tabella dei job in esecuzione, che può essere letta attraverso il comando interno jobs. Quando la shell avvia un processo sullo sfondo (ovvero in modo asincrono), emette una riga simile alla seguente, che indica rispettivamente il numero di job (tra parentesi quadre) e il numero dell'ultimo processo (il PID) del condotto associato a questo job:

[1] 12432

Si distinguono due tipi di job:

Un job è in primo piano quando è collegato alla tastiera e al video del terminale che si sta utilizzando; un job è sullo sfondo quando lavora in modo indipendente e asincrono rispetto all'attività del terminale.

Un job in esecuzione in primo piano può essere sospeso immediatamente attraverso l'invio del carattere di sospensione, che di solito si ottiene con [Ctrl z], in modo da avere di nuovo a disposizione l'invito della shell. In alternativa si può sospendere un job in esecuzione in primo piano, con ritardo, attraverso l'invio del carattere di sospensione con ritardo, che di solito si ottiene con [Ctrl y], in modo da avere di nuovo a disposizione l'invito della shell, ma solo quando il processo in questione tenta di leggere l'input dal terminale. È possibile gestire i job sospesi attraverso i comandi bg e fg. bg consente di fare riprendere sullo sfondo l'esecuzione del job sospeso, mentre fg consente di fare riprendere l'esecuzione del job sospeso in primo piano. kill consente di eliminare definitivamente il job.

Per fare riferimento ai job sospesi si utilizza il carattere %.

Riferimento
ai job
Descrizione
%n
Il simbolo % seguito da un numero fa riferimento al job con quel numero.
%prefisso
Il simbolo % seguito da una stringa fa riferimento a un job con un nome che inizia con quel prefisso. Se esiste più di un job sospeso con lo stesso prefisso si ottiene una segnalazione di errore.
%?stringa
Il simbolo % seguito da ? e da una stringa fa riferimento a un job con una riga di comando contenente quella stringa. Se esiste più di un job del genere si ottiene una segnalazione di errore.
%%
%+
Le notazioni %% o %+ fanno riferimento al job corrente dal punto di vista della shell, che corrisponde all'ultimo job sospeso quando questo si trovava a funzionare in primo piano.
%-
La notazione %- fa riferimento al penultimo job sospeso.
Utilizzando i comandi bg e fg, in mancanza di un riferimento esplicito al job, viene preso in considerazione quello «corrente» dal punto di vista della shell.

Segue la descrizione di alcuni esempi.

139.6   Esecuzione dei comandi

Dopo che un comando è stato suddiviso in parole, se il risultato è quello di un comando singolo, con eventuali argomenti, vengono eseguite le azioni seguenti.

Quando la shell ha determinato che si tratta di un eseguibile esterno ed è riuscita a trovarlo, vengono eseguite le azioni seguenti.

139.7   Configurazione di ambiente

Quando viene avviato un programma gli viene fornito un vettore di stringhe che rappresenta la configurazione dell'ambiente. Si tratta di una lista di coppie di nomi e valori loro assegnati, espressi nella forma seguente:

nome=valore

La shell permette di manipolare la configurazione dell'ambiente in molti modi. Quando la shell viene avviata, esamina la sua configurazione di ambiente e crea una variabile per ogni nome trovato. Queste variabili vengono rese automaticamente disponibili, nello stato in cui sono in quel momento, ai processi generati dalla shell. Questi processi ereditano così l'ambiente. Possono essere aggiunte altre variabili alla configurazione di ambiente attraverso l'uso del comando interno export, mentre è possibile eliminare delle variabili attraverso il comando interno unset.

Le variabili create all'interno della shell che non vengono esportate nell'ambiente, attraverso il comando export, o che non vengono create attraverso il comando declare (con l'opzione -x), non sono disponibili nell'ambiente dei processi discendenti (ovvero quelli generati durante il funzionamento della shell stessa).

Se si vuole fornire una configurazione di ambiente speciale all'esecuzione di un programma, basta anteporre alla riga di comando l'assegnamento di nuovi valori alle variabili di ambiente che si intendono modificare. L'esempio seguente avvia il programma mio_programma sullo sfondo con un percorso di ricerca diverso, senza però influenzare lo stato generale della configurazione di ambiente della shell.

PATH=/bin:/sbin mio_programma &[Invio]

139.8   Particolarità importanti della shell Bash

Bash è una shell POSIX con delle estensioni proprie, piuttosto sofisticate. Nei sistemi GNU, la shell Bash è normalmente quella predefinita ed è bene conoscere alcune particolarità di questa shell, perché non sempre viene configurata per un'aderenza stretta alle specifiche POSIX.

139.8.1   File di configurazione

La shell Bash messa in funzione a seguito di un accesso (login), se non è stata specificata l'opzione --noprofile:

Al termine della sessione di lavoro:

Quando la shell Bash funziona in modo interattivo, senza essere una shell di login, se non è stata specificata una delle opzioni --norc o --rcfile, sempre che esista, legge ed esegue il contenuto di ~/.bashrc.

Spesso si include l'esecuzione del contenuto del file ~/.bashrc anche nel caso di shell di login, attraverso un accorgimento molto semplice: all'interno del file ~/.bash_profile si includono le righe seguenti.

if [ -f ~/.bashrc ]
then
    . ~/.bashrc
fi

Il significato è semplice: viene controllata l'esistenza del file ~/.bashrc e se viene trovato viene caricato ed eseguito.

Quando la shell Bash viene utilizzata in modo non interattivo, ovvero per eseguire uno script, controlla il contenuto della variabile di ambiente BASH_ENV; se questa variabile non è vuota esegue il file nominato al suo interno.

In pratica, attraverso la variabile BASH_ENV si indica un file di configurazione che si vuole sia eseguito dalla shell prima dello script. In situazioni normali questa variabile è vuota, oppure non esistente del tutto.

Se l'eseguibile della shell Bash viene avviato con il nome sh (per esempio attraverso un collegamento simbolico), per quanto riguarda l'utilizzo dei file di configurazione si comporta come la shell Bourne, mentre per il resto il suo funzionamento è conforme alla shell POSIX.

Nel caso di shell di login, tenta di eseguire solo /etc/profile e ~/.profile, rispettivamente. L'opzione --noprofile può essere utilizzata per disabilitare la lettura di questi file di avvio.

Se l'eseguibile bash viene avviato in modalità POSIX, attraverso l'opzione --posix, allora la shell segue lo standard POSIX per i file di avvio. In tal caso, per una shell di login o interattiva viene utilizzato il nome del file contenuto nella variabile ENV.

Tabella 139.27. A seconda del modo con cui l'eseguibile della shell Bash viene avviato si utilizzano diversi tipi di file di configurazione.

Comando Tipo All'avvio Alla conclusione
bash
login /etc/profile, più
~/.bash_profile,
oppure ~/.bash_login,
oppure ~/.profile
~/.bash_logout
bash
interattiva ~/.bashrc
bash
non interattiva il file indicato nella variabile di ambiente BASH_ENV
sh
login /etc/profile, più
~/.profile
sh
interattiva il file indicato nella variabile di ambiente ENV
sh
non interattiva --
bash --posix
login il file indicato nella variabile di ambiente ENV
bash --posix
interattiva il file indicato nella variabile di ambiente ENV
bash --posix
non interattiva --

139.8.2   Opzioni

La shell Bash interpreta due tipi di opzioni: a carattere singolo e multicarattere. Le opzioni multicarattere devono precedere necessariamente quelle a carattere singolo.

Opzione Descrizione
--norc
Riguarda la modalità interattiva: non esegue il file di configurazione ~/.bashrc. Quando si avvia l'eseguibile della shell Bash utilizzando il nome sh per mantenere la compatibilità con la shell Bourne, questo file di configurazione non deve essere letto e questa opzione è sottintesa.
--noprofile
Riguarda la modalità interattiva di login: non esegue i file di inizializzazione /etc/profile, ~/.bash_profile, ~/.bash_login o ~/.bashrc.
--rcfile file
Riguarda la modalità interattiva: non esegue il file di inizializzazione personalizzato ~/.bashrc, ma quello indicato come argomento.
--version
Visualizza il numero di versione.
--login
Fa in modo che funzioni in qualità di shell di login.
--noediting
Quando è avviata in modalità interattiva, non usa la libreria GNU Readline per gestire la riga di comando.
--posix
Fa in modo di adeguarsi il più possibile alle specifiche POSIX.
-c stringa
Vengono eseguiti i comandi contenuti nella stringa. Eventuali argomenti successivi vengono passati ai parametri posizionali a partire dal parametro zero.
-i
Forza l'esecuzione in modalità interattiva.
-s
La shell legge i comandi dallo standard input.

139.8.3   Uso sommario della tastiera

La shell Bash fornisce un sistema di gestione della tastiera molto complesso, attraverso un gran numero di funzioni. Teoricamente è possibile ridefinire ogni tasto speciale e ogni combinazione di tasti a seconda delle proprie preferenze. In pratica, non è consigliabile un approccio del genere, dal momento che tutto questo serve solo per gestire la riga di comando.

La tabella 139.29 mostra un elenco delle funzionalità dei tasti e delle combinazioni più importanti.

Tabella 139.29. Elenco delle funzionalità dei tasti e delle combinazioni più importanti.

Comando Descrizione
Caratteri normali Inseriscono semplicemente i caratteri corrispondenti.
[Ctrl b] Sposta il cursore all'indietro di una posizione.
[Ctrl f] Sposta il cursore in avanti di una posizione.
[Backspace] Cancella il carattere alla sinistra del cursore.
[Ctrl d] Cancella il carattere corrispondente alla posizione del cursore.
[Ctrl a] Sposta il cursore all'inizio della riga.
[Ctrl e] Sposta il cursore alla fine della riga.
[Alt f] Sposta il cursore in avanti di una parola.
[Alt b] Sposta il cursore all'indietro di una parola.
[Ctrl l] Ripulisce lo schermo.

Generalmente funzionano anche i tasti freccia per spostare il cursore. In particolare, i tasti [freccia su] e [freccia giù] permettono di richiamare le righe di comando inserite precedentemente. Quando si preme un tasto o una combinazione non riconosciuta, si ottiene una segnalazione di errore.

Eventualmente si può intervenire nella configurazione della libreria Readline, attraverso il file /etc/inputrc oppure anche ~/.inputrc. L'esempio seguente si riferisce alla configurazione necessaria per l'uso ottimale di una console virtuale su un elaboratore con architettura x86.

# Abilita l'inserimento di caratteri a 8 bit.
set meta-flag           on

# Disabilita la conversione dei caratteri con l'ottavo bit attivo
# in sequenze di escape.
set convert-meta        off

# Abilita la visualizzazione di caratteri a 8 bit.
set output-meta         on

# Modifica l'abbinamento con i tasti rispetto a determinati comportamenti.
"\e[1~": beginning-of-line      # [home]                era C-a
"\e[4~": end-of-line            # [fine]                era C-e
"\e[3~": delete-char            # [canc]                era C-d
"\e[5~": backward-word          # [pagina su]           era M-b
"\e[6~": forward-word           # [pagina giù]          era M-f

139.8.4   Invito o «prompt»

Quando la shell funziona in modo interattivo, può mostrare due tipi di invito:

Il contenuto di queste variabili è una stringa che può essere composta da alcuni simboli speciali contrassegnati dal carattere di escape (\); i principali sono descritti nella tabella 139.31.

Tabella 139.31. Elenco di alcuni codici speciali per definire l'invito con la shell Bash.

Codice Descrizione
\t
Orario attuale nel formato hh:mm:ss (ore, minuti, secondi).
\d
Data attuale.
\n
Interruzione di riga.
\s
Nome della shell.
\w
Percorso assoluto della directory corrente.
\W
Nome finale del percorso della directory corrente (basename).
\u
Utente.
\h
Nome del nodo.
\#
Numero del comando attuale.
\!
Numero del comando nello storico.
\$
# se UID = 0; $ se UID > 0.
\nnn
Carattere corrispondente al numero ottale indicato.
\\
Una barra obliqua inversa singola (\).
\[
Inizio di una sequenza di controllo.
\]
Fine di una sequenza di controllo.

In particolare merita attenzione \$, il cui significato potrebbe non essere chiaro dalla descrizione fatta nella tabella. Rappresenta un simbolo che cambia in funzione del livello di importanza dell'utente: se si tratta di un UID pari a zero (se cioè si tratta dell'utente root) corrisponde al simbolo #, negli altri casi corrisponde al simbolo $.

La stringa dell'invito, dopo la decodifica dei codici di escape appena visti, viene eventualmente espansa attraverso i processi di sostituzione dei parametri e delle variabili, della sostituzione dei comandi, dell'espressione aritmetica e della suddivisione delle parole.

L'esempio seguente fa in modo di ottenere un invito che visualizza il nome dell'utente, il nome dell'elaboratore, la directory corrente e il simbolo $ o # a seconda del tipo di utente:

PS1='\u@\h:\w\$ '[Invio]

tizio@dinkel:~$

Disponendo di una shell Bash, è possibile costruire anche un invito dinamico, con l'ausilio di funzioni. Naturalmente, resta però la necessità di garantire una certa compatibilità anche con delle shell POSIX standard. L'esempio seguente rappresenta una porzione di codice che potrebbe essere inserita nel file /etc/profile:

dynamic_prompt () {
    if [ $? = 0 ]
    then
        echo ":)"
    else
        echo ":("
    fi
}
export -f dynamic_prompt
...
PS1="\u@\h:\w\\$ "
...
if [ "$BASH" != "" ]
then
    #
    # This is BASH.
    #
    PS1="\$(dynamic_prompt) $PS1"
fi
export PS1

Come si può vedere, la variabile di ambiente PS1 viene dichiarata inizialmente in modo compatibile con le shell standard, quindi, se si riesce a verificare che si tratta di una shell Bash, il contenuto della variabile viene modificato in modo da includere la funzione dynamic_prompt, che serve a mostrare un «sorriso» se l'ultimo comando è terminato restituendo il valore Vero.

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome shell_posix_utilizzo_generale.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!