Capitolo successivo Capitolo precedente Indice

25. Bash - comandi

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

25.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 stato 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 una esecuzione di un comando viene utilizzato in una espressione logica (booleana), si considera lo zero come equivalente a vero, mentre un qualunque altro valore viene considerato equivalente a falso.

In casi particolari è bash ad assegnare i valori di uscita di un comando:

Per conto suo, bash restituisce il valore di uscita dell'ultimo comando eseguito, a meno che non si verifichi un errore di sintassi, nel qual caso viene generato un valore diverso da zero (falso).

25.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 è diretto allo 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).

25.3 Lista di comandi

La lista di comandi è una sequenza di una o più pipeline separate da ;, &, && o ||, e terminata da ;, & o newline. 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 di della stessa lista che ha potuto essere eseguito.

Segue l'elenco degli operatori con la descrizione del loro significato.

;

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, e/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 carattere newline.

Esempi

# ./config ; make ; make install

Avvia in sequenza una serie di comandi per la compilazione e installazione di un ipotetico programma.

$ 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 non comporta alcuna differenza di comportamento.

---------

Di seguito si vedono due spezzoni di script equivalenti: nel secondo si sostituisce il punto e virgola con un carattere newline, 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ò sta alla sinistra è zero (vero), viene eseguito anche quanto sta alla destra.

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.

||

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.

Esempi

$ 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 in background. La descrizione del meccanismo con cui i programmi possono essere messi e gestiti in background viene fatta nella sezione `Controllo dei job'. 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 in background 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 in background.

# gpm -t ms &

Avvia il programma gpm di gestione del mouse in background.

(   )

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. Le assegnazioni 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.

{   }

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 è particolarmente indicato nella preparazione di shell. Gli esempi seguenti sono equivalenti.

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

---------

#!/bin/bash
{  mkdir ./prova
   cd ./prova
   ls
}

25.4 Alias

La gestione degli alias deriva da ksh.

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, bash cerca la prima parola di questo comando (quello che lo identifica) all'interno dell'elenco degli alias e se la trova lì, la sostituisce con il suo alias. La sostituzione non avviene se il comando o la prima parola di questo è protetta attraverso il quoting, ovvero se è 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 csh, 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 di bash, 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 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.

25.5 Ridirezione

Prima che un comando sia eseguito, si può ridirigere il suo input e/o 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 0). Se si utilizza il simbolo > da solo, la ridirezione si riferisce allo standard output (corrispondente al descrittore di file 1). 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, bash emette un errore.

Descrittore di file

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

Ridirezione dell'input

[n]< <parola>

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

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 esplicitamente indicato il descrittore dello standard input.

Ridirezione normale dell'output

[n]> <parola>

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

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 `set'). Se invece è attiva la modalità noclobber, si ottiene l'aggiunta di dati al file eventualmente esistente.

Per ottenere una sicura sovrascrittura di un file eventualmente preesistente, 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.

Ridirezione dell'output in aggiunta

[n]>> <parola>

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.

Ridirezione simultanea di standard output e standard error

&> <parola>

>& <parola>

bash consente la ridirezione di standard output e standard error in un unico file di destinazione (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.

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 indicata 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 per il quoting, si intende che il testo che verrà inserito non verrà espanso. Altrimenti, il testo viene espanso come di consueto.

Per maggiori dettagli conviene consultare la documentazione interna: bash(1).

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 0, cioè lo standard input.

Duplicazione di descrittori per l'output

[n]>& <parola>

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 1, 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.

Ridirezione in input/output

[n]<> <parola>

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.

Ridirezione e script

Lo standard input di uno script è quello del 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 ad un testo stampabile che viene inviato alla stampante predefinita.

#!/bin/bash
#======================================================================
# /var/spool/text/input-filter
#======================================================================
cat | /usr/local/bin/unix2dos | lpr

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 nello standard output.

#!/bin/bash
function ordina() {
        sort
}
ordina < ./elenco > ./elenco_ordinato

25.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 attualmente in esecuzione che può essere letta attraverso il comando interno jobs. Quando bash avvia un processo in modo asincrono (ovvero in background), 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 foreground quando utilizza la tastiera e il video del terminale che si sta utilizzando. Un job è in background quando lavora in modo indipendente e asincrono rispetto all'attività dal terminale.

Un job in esecuzione in foreground 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 il prompt di bash. In alternativa si può sospendere un job in esecuzione in foreground, 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 il prompt di bash, 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 l'esecuzione del job sospeso in background, mentre fg consente di fare riprendere l'esecuzione del job sospeso in foreground. 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, bash segnala un 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, bash segnala un errore.

%% | %+

Le notazioni %% o %+ fanno riferimento al job corrente dal punto di vista della shell, che corrisponde all'ultimo job sospeso quando era in foreground.

%-

La notazione %- fa riferimento al penultimo job sospeso.

Il controllo dei job è descritto anche nella sezione `Processi e job'.

Esempi

$ fg %1

Porta in foreground il job numero 1.

$ %1

Porta in foreground il job numero 1.

$ bg %1

Porta in background il job numero 1.

$ %1 &

Porta in background il job numero 1.

25.7 Esecuzione dei comandi

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

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

25.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 che quindi, ereditano 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'assegnazione di nuovi valori alle variabili di ambiente che si intendono modificare. L'esempio seguente avvia il programma mioprogramma in background con un diverso percorso di ricerca, senza però influenzare lo stato generale della configurazione di ambiente della shell.

$ PATH=/bin:/sbin mioprogramma &

 

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


Capitolo successivo Capitolo precedente Indice