LEZIONE del 18 Ottobre

Nella precedente lezione è stata introdotta la Bourne shell come esempio di linguaggio di comandi per l'ambiente Unix. Tuttavia, l'eccessiva semplicità di tale interprete va a discapito della chiarezza e immediatezza di lettura di molti "script" di shell, anche quando questi trattano problematiche standard. Allo scopo di mettere a disposizione dell'utente finale un ambiente di lavoro più amichevole il sistema operativo Linux ha sviluppato un interprete più sosfisticato, noto col nome di bash shell.

Nel corso della lezione verranno ripresi ed ampliati i costrutti linguistici già visti per la Bourne shell. Inoltre, verranno considerati ed approfondite le proprietà dei parametri di shell.

GRAMMATICA DELLA SHELL

L'interprete dei comandi di un sistema operativo è l'interfaccia testuale fra l'utente e il sistema. In questo modo l'utente finale è in grado di accedere a tutte le funzionalità delle risorse disponibili dell'elaboratore. Tale interprete è uno degli strumenti essenziali che l'amministratore del sistema impiega per la sua gestione.

Come ogni linguaggio, anche l'inteprete dei comandi, o shell, è caratterizzato da un insieme di comandi base, una serie di regole per costruire comandi composti entrambi potendo utilizzare variabili, ossia identificatori esplicitamente impiegati per trasferire valori da un comando all'altro.

Comandi semplici

Un comando semplice è una sequenza opzionale di assegnazioni di variabile seguita da parole, separate da blank, e ridirezioni, e terminati da un operatore di controllo. La prima parola specifica il comando che deve essere eseguito. Le rimanenti parole sono passate come argomenti per il comando chiamato.

Il valore di ritorno di un comando semplice è il suo stato di uscita, o 128+n se il comando è terminato dal segnale n.

Pipeline

Una pipeline è una sequenza di uno o più comandi separati dal carattere |. Il formato per una pipeline è:

[ ! ] comando [ | comando2 ... ]

Lo standard output di comando è connesso allo standard input di comando2. Questa connessione è effettuata prima di qualsiasi ridirezione specificata dal comando.

Se la parola riservata ! precede una pipeline, lo stato di uscita di quella pipeline è il NOT logico dello stato di uscita dell'ultimo comando. Altrimenti, lo stato della pipeline è lo stato di uscita dell'ultimo comando. La shell aspetta che tutti i comandi nella pipeline terminino, prima di ritornare un valore.

Ogni comando in una pipeline è eseguito come un processo separato (cioè, in una subshell).

Liste

Una lista è una sequenza di una o più pipeline separate da uno degli operatori di controllo ;, &, &&, o ||, e terminata da uno di ;, &, o <newline>.

Di questi operatori di lista, && e || hanno uguali precedenze, seguiti da ; e &, che hanno uguali precedenze.

Se un comando è terminato dall'operatore di controllo &, la shell esegue il comando in background in una subshell. La shell non aspetta che il comando finisca, e lo stato di ritorno è 0. I comandi separati da un ; sono eseguiti sequenzialmente; la shell aspetta che ogni comando, a turno, termini. Lo stato di ritorno è lo stato di uscita dell'ultimo comando eseguito.

L'operatore di controllo && e || denotano liste AND e liste OR, rispettivamente. Una lista AND ha la forma

comando && comando2

comando2 è eseguito se, e solo se, comando ritorna uno stato di uscita di zero.

Una lista OR ha la forma

comando || comando2

comando2 è eseguito se, e solo se, comando ritorna uno stato di uscita diverso da zero. Lo stato di ritorno di liste AND e OR è lo stato di uscita dell'ultimo comando eseguito nella lista.

Comandi Composti

Un comando composto è uno dei seguenti:

Parametri

Un parametro è una entità che immagazzina valori, qualcosa come una variabile in un linguaggio di programmazione convenzionale. Esso può essere un nome, un numero, o uno dei caratteri speciali elencati come parametri speciali. Per gli scopi della shell, una variabile è un parametro indicato da un nome.

Un parametro risulta impostato se ad esso è stato assegnato un valore. La stringa nulla è un valore valido. Una volta che una variabile è impostata, essa può essere eliminata solo usando il comando predefinito unset.

Una variabile può essere assegnata da una istruzione della forma

nome=[valore]

Se valore non viene dato, alla variabile viene assegnata la stringa nulla. Tutti i valori sono sottoposti alla espansione della tilde, espansione di parametro e variabile, sostituzione di comando, espansione aritmetica, e rimozione dei caratteri di quotatura. Se la variabile ha il suo attributo assegnato allora valore è sottoposto alla espansione aritmetica perfino se la sintassi $[...] non appare.

La suddivisione in parole non viene effettuata, con l'eccezione $@ come spiegato più avanti per i Parametri speciali. L'espansione del pathname in questo caso non viene effettuata.

Parametri Posizionali

Un parametro posizionale è un parametro indicato da una o più cifre, diverse dalla singola cifra 0. I parametri posizionali sono assegnati dagli argomenti della shell quando viene chiamata, e possono essere riassegnati usando il comando predefinito set.

I parametri posizionali non possono essere assegnati con istruzioni di assegnazione. I parametri posizionali sono temporaneamente sostituiti quando viene eseguita una funzione di shell.

Quando si espande un parametro posizionale consistente di più di una sola cifra, esso deve essere racchiuso tra parentesi graffe.

Parametri Speciali

La shell tratta molti parametri in modo speciale. Questi parametri possono solo essere referenziati; il loro assegnamento non è permesso.

Esempi d'Uso della Shell

Negli esempi che seguono sono riportati alcune caratteristiche salienti sia della Bourne shell che della C shell. In essi compaiono alcuni comandi di utilità generale il cui impiego e spiegazione dettagliaia possono ottenersi consultando il manuale in linea con man < nomecomando>.

Cancella un File o un Direttorio

Il seguente programma permette di cancellare indifferentemente file o direttori, trasferendoli temporaneamente in un direttorio cestino dal quale possono essere recuperati o definitivamente rimossi. La parte iniziale del programma serve a verificare il numero e la correttezza dei parametri passati. Vengono uitlizzati i programmi di utilità generale sed e expr.

#
# delete options file
# delete options dir
#
NAMES=""
K=0
OPTIONS=""
for ITEM in $*
do
  if [ `echo $ITEM | egrep -e "-"` ] > /dev/null;
    then OPTIONS="$OPTIONS `echo $ITEM | sed s/-//`"
    else
      K=`expr $K + 1`
      NAMES="$ITEM $NAMES"
  fi
done
if [ $K -eq 0 ];
  then
    echo "insufficient parameters"
    exit 1
fi
OPT=""
MINUS="-"
VERBOSE=0
for A in $OPTIONS
do
  if [ $A = "v" ] > /dev/null;
  then VERBOSE=1
  else
    OPT="$MINUS$A $OPT"
  fi
done
for NAME in $NAMES
do
  if [ $VERBOSE -eq 0 ] > /dev/null;
    then mv $OPT $NAME $HOME/.wastebasket
    else
      echo "delete $NAME (y/n) \c"
      read YES
      if [ $YES = "y" ] > /dev/null;
        then mv $OPT $NAME $HOME/.wastebasket
      fi
  fi
done
Cancellazione di un File

Rinomina un File oppure spostalo in un Direttorio

Il seguente programma estende il comando mv spostando il file passato come primo argomento nel direttorio indicato come secondo, eventualmente creandolo se questo non esiste. La parte iniziale del programma serve a verificare il numero e la correttezza dei parametri passati. Vengono utilizzati i programmi di utilità generale sed e expr.

#
# move options file dest
#
NAMES=""
K=0
OPTIONS=""
for ITEM in $*
do
  if [ `echo $ITEM | egrep -e "-"` ] > /dev/null;
    then OPTIONS="$OPTIONS `echo $ITEM | sed s/-//`"
    else
      K=`expr $K + 1`
      NAMES="$ITEM $NAMES"
  fi
done
if [ $K -le 1 ];
  then
    echo "insufficient parameters"
    exit 1
fi
OPT=""
MINUS="-"
for A in $OPTIONS
do
  OPT="$MINUS$A $OPT"
done
K=1
for NAME in $NAMES
do
  if [ $K -eq 2 ];
    then FNAME=$NAME
    else DEST=$NAME
  fi
  K=`expr $K + 1`
done
if [ ! -d $DEST ] && \
   [ `echo $DEST | egrep -e "/"` ] > /dev/null;
  then mkdir $DEST
fi
mv $OPT $FNAME $DEST
Rinomina un File

Tipo di File

Il seguente programma restituisce, per ogni file, le sue caratteristiche essenziali: leggibile, eseguibile, scrivibile, vuoto, direttorio, ecc...

#!/bin/bash
#  ftype: Display the type of file
#     Synopsis : ftype <filename>
#
if [ "$#" != 1  -o  "$1" == "" ]
  then
    FILE="."
  else
    FILE="$1"
fi
echo "$FILE" is:
if [ -r "$FILE" ]
  then
    echo -n "readable, "
fi
if [ -w "$FILE" ]
  then
    echo -n "writable, "
fi
if [ -x "$FILE" ]
  then
  if [ -d "$FILE" ]
    then
      echo -n "searchable, "
    else
      echo -n "executable, "
  fi
fi
if [ -f "$FILE" ]
  then
    echo -n "regular file "
fi
if [ -d "$FILE" ]
  then
    echo -n "directory "
fi
if [ -z "$FILE" ]
  then
    echo "and it is empty"
  else
    echo "and it is not empty"
fi
exit 0
Tipo di File

L'esempio proposto utilizza il comando test <expr>, nella forma [ <expr], per "leggere" i flag di stato dei file.