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.
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.
Uno spazio o carattere di tabulazione (tab).
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 { }
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.
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.
Il file cercato in PATH non ha bisogno di essere eseguibile. La ricerca è fatta nella directory corrente se nessun file viene trovato in PATH. Se sono forniti degli argomenti, essi diventano i parametri posizionali quando nomefile è eseguito. Altrimenti i parametri posizionali sono inalterati. Lo stato di ritorno è lo stato dell'ultimo comando terminato dentro lo script (0 se nessun comando è eseguito), e falso se namefile non viene trovato.
Uno spazio finale in valore provoca il controllo della successiva parola per la sostituzione di alias quando l'alias è espanso. Per ogni nome nella lista di argomenti per cui nessun valore è fornito, è stampato il nome e valore dell'alias.
Alias ritorna vero a meno che sia dato un nome per il quale nessun alias è stato definito.
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>.
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 |
In quest'esempio come in altri appare il trattamento esplicito delle opzioni e dei parametri passati al comando.
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 |
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.
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 |
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.