LEZIONE del 25 Ottobre

Per un corretto e proficuo utilizzo della shell è necessario avere una discreta conoscenza sia della sintassi del suo linguaggio che del significato di alcuni suoi costrutti speciali. Nella lezione precedente si è dato conto della strutturazione dei comandi composti con alcuni accenni al calcolo aritmetico e ai comandi predefiniti, questi ultimi trattati diffusamente nel seguito.

GRAMMATICA DELLA SHELL

I costrutti linguistici su cui si basa l'interprete della shell richiedono la preventiva definizione degli elementi che costituiscono le righe di comando, il modo con cui sono interpretate le parole predefinite e i metodi per by-passare, se necessario, l'interpretazione. La parte relativa ai comandi predefiniti è intesa a dare un quadro esauriente delle potenzialità della shell.

Definizioni

Parole Riservate

Le parole riservate sono parole che hanno un significato speciale per la shell. Le seguenti parole sono riconosciute come riservate quando non quotate e sono la prima parola di un comando semplice o la terza parola di un comando case o di un comando for:

! case do done elif else esac fi for function if in select then until while { }

Quotatura

La quotatura è usata per togliere il significato speciale, per la shell, di certi caratteri o parole. La quotatura è utilizzata normalmente per disabilitare il trattamento dei caratteri speciali, per prevenire che le parole riservate siano riconosciute come tali, e per prevenire l'espansione di parametro.

Ciascuno dei metacaratteri precedentemente elencati ha un significato speciale per la shell e deve essere quotato se esso deve rappresentare se stesso. Vi sono tre meccanismi di quotatura: caratteri di escape, apostrofi, e virgolette.

Un backslash (\) non quotato è il carattere di escape. Esso conserva il valore letterale del successivo carattere, con l'eccezione di <newline>. Se vi è una coppia \<newline>, e il backslash non è quotato, la sequenza \<newline> è trattato come una continuazione di linea (cioè, viene realmente ignorata).

Racchiudere caratteri in apostrofi conserva il valore letterale di ogni carattere all'interno. Un apostrofo non può trovarsi tra apostrofi, nemmeno quando preceduto da un backslash.

Racchiudere caratteri in virgolette conserva il valore letterale di tutti i caratteri all'interno, con le eccezioni di $, ` e \. I caratteri $, ` conservano il loro significato speciale anche tra virgolette. Il backslash mantiene il suo significato speciale solo quando seguito da uno dei seguenti caratteri: $, `, ", \ o <newline>. Le virgolette possono essere racchiuse fra virgolette facendole precedere da un backslash.

I parametri speciali * e @ hanno un significato speciale quando sono tra virgolette.

Comandi Predefiniti

I comandi riportati di seguito sono predefiniti nella shell. Per una loro dettagliata descrizione, l'impiego delle opzioni e le caratteristiche dei parametri, si faccia riferimento al manuale della shell.

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

Ripulisci un Direttorio

Il seguente programma permette di cancellare selettivamente i file contenuti nel direttorio passato come parametro, in base alle estensioni passate come opzioni non standard. 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.

#!/bin/bash
# dclean options dirname
#
K=0
OPTIONS=""
for arg in $*
do
  if [ `echo $arg | egrep -e "-"` ] > /dev/null;
    then OPTIONS="$OPTIONS `echo $arg | sed s/-//`"
    else
      K=`expr $K + 1`
      NAME=$arg
  fi
done
if [ $K -eq 0 ];
  then
    echo "insufficient parameters"
    exit 1
fi
if [ $K -ge 2 ];
  then
    echo "too many parameters"
    exit 1
fi
if [ ! -d $NAME ]; then
  echo "no directory given"
  exit 2
fi
OPT=""
EXTENSIONS=""
MINUS="-"
for A in $OPTIONS
  do
    if [ $A = i ] > /dev/null; then OPT="$MINUS$A $OPT"
    elif [ $A = f ] > /dev/null; then OPT="$MINUS$A $OPT"
    else EXTENSIONS="$EXTENSIONS $A"
    fi
  done
for EXT in $EXTENSIONS
  do
    /bin/rm $OPT $NAME/*.$EXT
  done
exit 0

Ripulitura di un Direttorio

In quest'esempio come in altri appare il trattamento esplicito delle opzioni e dei parametri passati al comando.

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.

#!/bin/bash
# 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

Sposta un file in un Direttorio

Quest'ultimo esempio, come il precedente, sono analoghi perchè sono implementati sui comandi di shell disponibili (mv e mkdir per l'uno e rm per l'altro) con l'unica differenza che nel primo esempio le opzioni (tranne alcune ben specificate) sono interpretate come file extension.

Aggiunta di nuovi utenti

Il programma proposto di seguito realizza un'interfaccia sufficiente amichevole per il comando useradd permettendo all'amministratore di sistema di aggiungere facilmente nuovi utenti autorizzati all'uso del sistema. Poichè si tratta di costruire, per ogni nuovo utente, l'area di lavoro e aggiornare il database delle autorizzazione di accesso, è necessario richiedere le informazioni necessarie durante la fase di creazione.

#!/bin/bash
# Add a new user, by providing the necessary information
# Return codes:
#  0: success 
#  1: can't update password file 
#  2: invalid command syntax 
#  3: invalid argument to option 
#  4: UID already in use (and no -o) 
#  6: specified group doesn't exist 
#  9: username already in use 
# 10: can't update group file 
# 12: can't create home directory 
# 13: can't create mail spool
#
export CRYPTING=1;
export GROUP_DB="/etc/group";
export BASE_DIR="/home/fedora7";

export FullName;
export UId;
export GId;
export PASSWD;
export RETVAL;

  function NewUser() {
    RESULT=0
    echo "================== New User Information ===================="
    read -p 'Real User Name: ' FullName;
    if [ -z "$FullName" ] ; then
      echo "Real User Name is needed";
      RESULT=21
    fi
    read -p 'User Identifier (uid): ' UId;
    if [ -z "$UId" ] ; then
      echo "User Identifier is needed";
      RESULT=22
    fi
    GroupInfo=(`grep -e "$Group" "$GROUP_DB" | sed 's/:/ /g'`);
    GroupName=${GroupInfo[0]};
    GroupId=${GroupInfo[2]};
    if [ "$GroupName" == "$Group" ]; then
      GId="$GroupId"
    else
      read -p 'Group Identifier (gid): ' GId;
      if [ -z "$GId" ] ; then
        echo "Group Identifier is needed";
        RESULT=23
      fi
    fi;
    stty -echo    # Turns off screen echo.
    echo -n "Password : "
    read PASSWD1
    stty echo ;   # Restores screen echo.
    if [ -z "$PASSWD1" ] ; then
      echo "User Password is required";
      RESULT=24
    fi
    echo
    stty -echo    # Turns off screen echo.
    echo -n "Retype Password : "
    read PASSWD2
    stty echo ;   # Restores screen echo.
    if [ "$PASSWD1" != "$PASSWD2" ] ; then
      echo " Passwords are not equal";
      RESULT=25
    fi
    echo
    PASSWD="$PASSWD2"
    echo "============================================================"
    return $RESULT
  } 

  function MakeUser() {
    echo -e "The following user is going to be created";
    echo -e "User Full Name : \"$FullName\""
    echo -e "Login Name : \t \"$LoginName\""
    echo -e "UId : \t\t $UId"
    echo -e "GId : \t\t $GId"
    L=${#PASSWD};
    echo -e -n "PASSWD : \t "
    while [ "$L" -gt 0 ] ; do
      echo -n "*"
      let "L = $L - 1"
    done
    case $CRYPTING in
    0) PASSWD=`openssl passwd -crypt $PASSWD`;;
    1) PASSWD=`openssl passwd -1 $PASSWD`;;
    esac
    echo
    echo -e "base dir : \t ${BASE_DIR}";
    echo -e "login shell : \t $SHELL";

    echo
    echo -n "Do I create user ${LoginName}? "
    read YES;
    if [ "$YES" == "y" -o "$YES" == "Y" ] ; then
      /usr/sbin/useradd -c "$FullName" -K GID_MIN=0 -u $UId \
                        -p $PASSWD -g $GId -s $SHELL -m $LoginName ;
      RETVAL=$? ;
    fi
    return $RETVAL
  }

function UserDel() {
  userdel -r $LoginName;
  RETVAL=$?;
  return $RETVAL
}

export -f NewUser
export -f MakeUser
export -f UserDel
export LoginName
export Group

if [ "$#" -lt 2  ] ; then
  echo "insufficient parameters"
  echo "Synopsis: newuser -<action> <loginname>"
  echo "   <action> = a  [add a new user]"
  echo "   <action> = d  [delete an user]"

  exit 1
fi

ACTION=`echo $1 | sed 's/-//g'`;
LoginName="$2"
Group="$3"

case $ACTION in
a) NewUser;
   RETVAL=$?;;
d) echo -n "Do I delete user ${LoginName} in the group ${Group}? "
   read YES;
   if [ "$YES" == "y" -o "$YES" == "Y" ] ; then
     RETVAL=$?
   fi;;
*) echo "action \"$ACTION\" is not yet implemented";;
esac

if [ "$RETVAL" == "0" ] ; then
  echo "User and Group manipulation require root privileges"
  case $ACTION in
    a) su -c MakeUser ;;
    d) su -c UserDel ;;
  esac
fi

echo "exiting..."
exit $RETVAL

Aggiungi/Elimina un Utente dal Sistem

L'esempio proposto si basa sui valori di default contenuti nel file useradd posizionato, come tutti i file di configurazione, nella directory /etc; nel caso specifico, si tratta della directory /etc/default. Poichè il comando proposto è effettivamente utilizzabile, contiene il meccanismo che consente l'esecuzione di comandi in modalità superutente mediante la richiesta della relativa password. Inoltre, poichè gli accessi devono essere protetti, la password del nuovo utente viene richiesta in modalità nascosta per essere opportunamente cifrata nel database shadow delle password.