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.