[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]


46. Bash: comandi

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.

46.1 Exit status o valore restituito dai comandi

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:

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

46.2 Pipeline

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

46.3 Lista di comandi

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.

46.3.1 Separatore di comandi «;»

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.

Esempi

./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"

46.3.2 Operatore di controllo «&&»

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.

Esempi

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.

46.3.3 Operatore di controllo «||»

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.

Esempi

mkdir ./prova || mkdir ./prova1

Si tenta di creare la directory `prova/', se il comando fallisce si tenta di creare `prova1/' al suo posto.

46.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 46.6. Dal momento che non si attende la loro conclusione per passare all'esecuzione di quelli successivi, il valore restituito è sempre zero.

Esempi

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 &

Avvia il programma `gpm' di gestione del mouse sullo sfondo.

46.3.5 Delimitatori di lista «(...)»

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.

Esempi

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

Crea la directory `prova/' o `prova1/'. Se ci riesce, visualizza il messaggio.

46.3.6 Delimitatori di lista «{...}»

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.

Esempi

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
}

46.4 Alias

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

Esempi

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.

46.5 Ridirezione

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.

46.5.1 Descrittore di file

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

46.5.2 Ridirezione dell'input

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

Esempi

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.

46.5.3 Ridirezione normale dell'output

[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' 48.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 `>|'.

Esempi

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.

46.5.4 Ridirezione dell'output in aggiunta

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

Esempi

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.

46.5.5 Ridirezione simultanea di standard output e standard error

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

Esempi

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.

46.5.6 Ridirezione «here document»

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

46.5.7 Duplicazione di descrittori per l'input

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

46.5.8 Duplicazione di descrittori per l'output

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

Esempi

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.

46.5.9 Ridirezione in input/output

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

46.5.10 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 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

46.5.11 Ridirezione e funzioni

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

46.6 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 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 `%'.

Riferimento ai job

%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 30.1.

Esempi

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 &

Porta sullo sfondo il job numero uno.

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

46.8 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 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 &

---------------------------

Appunti Linux 2000.04.12 --- Copyright © 1997-2000 Daniele Giacomini --  daniele @ pluto.linux.it


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.


[inizio] [indice generale] [precedente] [successivo] [indice analitico] [contributi]