[>a266.html>] [<a264.html<] [^a2.html^]
Con il termine «comando» si intendono diversi tipi di entità che hanno in comune il modo con cui vengono utilizzati: attraverso un nome seguito eventualmente da alcuni argomenti. Può trattarsi dei casi seguenti.
Detti anche comandi di shell, sono delle funzioni predefinite all'interno della shell.
Dette anche funzioni di shell, sono funzioni scritte all'interno di uno script di shell.
Sono dei nomi associati ad altri comandi, di solito con l'aggiunta di qualche argomento. In maniera semplificata, possono essere visti come un modo diverso per identificare comandi già esistenti.
Detti anche comandi esterni perché non sono contenuti nella shell che li avvia.
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.
In casi particolari è la shell che assegna i valori di uscita di un comando:
quando un programma viene interrotto a causa di un segnale (di interruzione), il suo valore di uscita è pari a 128 più il valore di quel segnale;
quando un programma non viene trovato e quindi non può essere avviato, il suo valore di uscita è 127;
quando un presunto programma viene trovato, ma non risulta eseguibile, il suo valore di uscita è 126.
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).
La pipeline è una sequenza di uno o più comandi separati dal simbolo pipe, ovvero la barra verticale (|). Il formato normale per una pipeline è 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 pipeline basta anche un solo comando.
Normalmente, il valore restituito dalla pipeline corrisponde a quello dell'ultimo comando che viene eseguito all'interno di questa.
Se all'inizio della pipeline viene posto un punto esclamativo (!), il valore restituito corrisponde alla negazione logica del risultato normale.
La shell attende che tutti i comandi della pipeline siano terminati prima di restituire un valore.
Ogni comando in una pipeline è eseguito come un processo separato (cioè, in una subshell).
La lista di comandi è una sequenza di una o più pipeline separate da ;, &, && o ||, e 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 ha potuto essere eseguito.
Nelle sezioni seguenti vengono descritti questi operatori.
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.
#
./config ; make ; make install
Avvia in sequenza una serie di comandi per la compilazione e installazione di un programma ipotetico.
$
echo "uno" ; echo "due"
$
echo "uno" ; echo "due" ;
I due comandi sono equivalenti: nel secondo la lista viene conclusa con un punto e virgola, ma ciò non produce alcuna differenza di comportamento.
Di seguito si vedono due pezzi di script equivalenti: nel secondo si sostituisce il punto e virgola con un codice di interruzione di riga, dato che il contesto lo consente.
ls ; echo "Ciao a tutti"
ls echo "Ciao a tutti"
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.
$
mkdir ./prova && echo "Creata la directory prova"
Viene eseguito il comando mkdir ./prova. Se ha successo viene eseguito il comando successivo che visualizza un messaggio di conferma.
L'operatore di controllo || si comporta come l'operatore booleano OR: se il valore di uscita di ciò 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.
$
mkdir ./prova || mkdir ./prova1
Si tenta di creare la directory prova/
, se il comando fallisce si tenta di creare prova1/
al suo posto.
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 47.6. Dal momento che non si attende la loro conclusione per passare all'esecuzione di quelli successivi, il valore restituito è sempre zero.
$
yes > /dev/null & echo "yes sta funzionando"
Il programma yes viene messo in esecuzione sullo sfondo e di seguito viene visualizzato un messaggio. Al termine dell'esecuzione della lista, yes continua a funzionare.
$
echo "yes sta per essere avviato" ; yes > /dev/null &
In questo caso viene prima emesso il messaggio e quindi viene avviato yes sullo sfondo.
#
gpm -t ms &
Le liste, o parti di esse, possono essere racchiuse utilizzando delle parentesi tonde.
( lista )
La lista racchiusa tra parentesi tonde 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.
$
(mkdir ./prova || mkdir ./prova1) && echo "Creata la directory"
Crea la directory prova/
o prova1/
. Se ci riesce, visualizza il messaggio.
Le liste possono essere raggruppate utilizzando delle parentesi graffe.
{ lista ; ... }
Le liste contenute tra parentesi graffe 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/bash { mkdir ./prova ; cd ./prova ; ls ; }
#!/bin/bash { mkdir ./prova cd ./prova ls }
La gestione degli alias deriva dalla shell Korn.
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.
Se l'ultimo carattere del testo di rimpiazzo dell'alias è uno spazio o una tabulazione, allora anche la parola successiva viene controllata per una possibile sostituzione attraverso gli alias.
A differenza della shell C, non c'è modo di utilizzare argomenti attraverso gli alias. Se necessario, conviene utilizzare le funzioni.
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. |
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
.
#
alias rm="rm -i"
Crea un alias al comando (programma) rm in modo che venga eseguito automaticamente con l'opzione -i che implica la richiesta di conferma per ogni file che si intende cancellare.
#
alias cp="cp -i"
Crea un alias al comando (programma) cp in modo che venga eseguito automaticamente con l'opzione -i, cosa che implica la richiesta di conferma per ogni file che si intende eventualmente sovrascrivere.
#
alias mv="mv -i"
Crea un alias al comando (programma) mv in modo che venga eseguito automaticamente con l'opzione -i che implica la richiesta di conferma per ogni file che si intende eventualmente sovrascrivere.
#
alias spegni="shutdown -h -t 5 now"
Crea l'alias spegni per abbreviare il comando di spegnimento normale.
Prima che un comando sia eseguito, si può ridirigere il suo input e il suo output utilizzando una speciale notazione 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 viene segnalato un errore.
Si distinguono tre tipi di descrittori di file per l'input e l'output:
[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.
$
sort < ./elenco
Emette il contenuto del file elenco
(che si trova nella directory corrente) riordinando le righe. sort riceve il file da ordinare dallo standard input.
$
sort 0< ./elenco
Esegue la stessa cosa dell'esempio precedente, con la differenza che viene indicato esplicitamente il descrittore dello standard input.
[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, sempre che non sia attiva la modalità noclobber; (si veda il comando interno set 49.31). Se invece è attiva la modalità noclobber, si ottiene l'aggiunta di dati al file eventualmente esistente. Per garantire la sovrascrittura di un file che potrebbe esistere già, si può utilizzare l'operatore di ridirezione >|.
$
ls > ./dir.txt
Crea il file dir.txt
nella directory corrente e gli inserisce l'elenco dei file della directory corrente.
$
ls 1> ./dir.txt
Esegue la stessa operazione dell'esempio precedente con la differenza che il descrittore che identifica lo standard output viene indicato esplicitamente.
$
ls 1>| ./dir.txt
Esegue la stessa operazione del primo esempio, ma si indica in maniera inequivocabile che il file dir.txt
deve essere creato, anche se è attiva la modalità noclobber.
$
ls XtgEWSjhy * 2> ./errori.txt
Crea il file errori.txt
nella directory corrente e gli inserisce i messaggi di errore generati da ls quando si accorge che il file XtgEWSjhy
non esiste.
[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.
$
ls >> ./dir.txt
Aggiunge al file dir.txt
l'elenco dei file della directory corrente.
$
ls 1>> ./dir.txt
Esegue la stessa operazione dell'esempio precedente con la differenza che il descrittore che identifica lo standard output viene indicato esplicitamente.
$
ls XtgEWSjhy * 2>> ./errori.txt
Aggiunge al file errori.txt
i messaggi di errore generati da ls quando si accorge che il file XtgEWSjhy
non esiste.
&> file
>& file
La shell Bash consente la ridirezione di standard output e standard error in un file di destinazione unico (quello rappresentato dalla parola che segue il simbolo di ridirezione).
La prima delle due notazioni è preferibile. |
Non è possibile sfruttare questo meccanismo per accodare dati a un file esistente. |
$
ls XtgEWSjhy * &> ./tutto.txt
Crea il file tutto.txt
e gli inserisce il messaggio di errore causato dal file XtgEWSjhy
inesistente e l'elenco dei file della directory corrente.
<<[-] parola
Si tratta di un tipo di ridirezione particolare e poco usato. Istruisce la shell di leggere dallo standard input fino a quando viene incontrata la parola indicata (senza spazi iniziali). 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 indicata racchiusa tra virgolette, quelle usate per la protezione delle stringhe, si intende che il testo che verrà inserito non deve essere espanso. Altrimenti, il testo viene espanso come di consueto.
Per maggiori dettagli conviene consultare la documentazione interna: bash.info oppure bash(1).
[n]<&parola
Con la notazione sopra indicata si ottiene la duplicazione della ridirezione dell'input. Se la parola indicata si espande generando una o più cifre numeriche, il descrittore di file corrispondente a questo numero viene copiato nel descrittore n. Se l'espansione della parola indicata genera un trattino, il descrittore n viene chiuso. Se il descrittore n non viene specificato, si intende zero, cioè lo standard input.
[n]>&descrittore
Con la notazione sopra indicata si ottiene la duplicazione della ridirezione dell'output. L'output del descrittore n viene aggiunto all'output del descrittore rappresentato dalla parola. Se il descrittore n non viene indicato, si intende il numero uno, cioè lo standard output.
$
ls XtgEWSjhy * > ./tutto.txt 2>&1
Crea il file tutto.txt
nella directory corrente e gli inserisce i messaggi di errore generati da ls quando si accorge che il file XtgEWSjhy
non esiste, insieme all'elenco dei file esistenti.
[n]<> file
La notazione precedente permette di aprire il file indicato dalla parola, in lettura e scrittura, collegando i due flussi al descrittore n. Se questo descrittore non è indicato si intende l'utilizzo di entrambi standard input e standard output.
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 di 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, e quindi, attraverso una pipeline si arriva a un testo stampabile che viene inviato alla stampante predefinita.(1)
#!/bin/bash #====================================================================== # /var/spool/text/input-filter #====================================================================== cat | /usr/bin/unix2dos | lpr
Con lo stesso ragionamento attraverso il quale si può creare uno script in grado di ricevere l'input dallo standard input, si può realizzare una funzione all'interno dello script in grado di essere usata come un programma che trasforma lo standard input in standard output ed eventualmente anche standard error.
Nell'esempio seguente, la funzione ordina() non fa altro che riordinare quanto proveniente dallo standard input emettendone il risultato attraverso lo standard output.
#!/bin/bash function ordina() { sort } ordina < ./elenco > ./elenco_ordinato
Il controllo dei job si riferisce alla possibilità di sospendere e ripristinare selettivamente l'esecuzione dei processi. La shell associa un job a ogni pipeline. 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,
[1] 12432
che indica rispettivamente il numero di job (tra parentesi quadre) e il numero dell'ultimo processo (il PID) della pipeline associato a questo job.
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 %.
%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 era in primo piano.
%-
La notazione %- fa riferimento al penultimo job sospeso.
Il controllo dei job è descritto anche nella sezione 31.1. |
$
fg %1
Porta in primo piano il job numero uno.
$
%1
Porta in primo piano il job numero uno.
$
bg %1
Porta sullo sfondo il job numero uno.
$
%1 &
Dopo che un comando è stato suddiviso in parole, se il risultato è quello di un comando singolo, con eventuali argomenti, vengono eseguite le azioni seguenti.
Se il nome del comando contiene una o più barre (/), questo viene inteso essere un percorso del file system, e di conseguenza il comando è inteso riferirsi precisamente a un file eseguibile, per cui la shell tenta di avviarlo.
Se il nome del comando non contiene alcuna barra (/):
se esiste una funzione di shell con quel nome, questa viene eseguita;
se esiste un comando interno con quel nome, questo viene eseguito;
viene cercato all'interno del percorso di ricerca degli eseguibili contenuto nella variabile PATH.
Se la ricerca fallisce si ottiene una segnalazione di errore e la restituzione di un valore di uscita diverso da zero.
Quando la shell ha determinato che si tratta di un eseguibile esterno ed è riuscita a trovarlo, vengono eseguite le azioni seguenti.
La shell avvia il programma configurando gli argomenti nel modo consueto: il primo, cioè zero, contiene il nome del programma, quelli successivi, contengono gli argomenti forniti eventualmente nella riga di comando.
Se non si tratta di un programma e nemmeno di una directory (in tal caso verrebbe comunque emessa una segnalazione di errore), viene inteso essere uno script di shell. In tal caso viene generata una subshell per la sua esecuzione, la quale si reinizializza in modo da presentare allo script una situazione simile a quella di una nuova shell.
Se il programma è un file di testo che inizia con #!, si intende che si tratti di uno script che deve essere interpretato attraverso il programma indicato nella parte restante della prima riga. La shell eseguirà quindi quel programma dando come argomenti il nome dello script e altri eventuali argomenti ricevuti nella riga di comando originale.
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 dei comandi interni export e declare (quest'ultimo con l'opzione -x), 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 diverso percorso di ricerca, senza però influenzare lo stato generale della configurazione di ambiente della shell.
$
PATH=/bin:/sbin mio_programma &
1) 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, e restituisce il risultato dallo standard output.