LEZIONE del 13 Novembre

A completamento degli argomenti trattati nelle precedenti lezioni e che si riferiscono alla composizione di comandi semplici, all'impiego di variabili d'ambiente e alla disponibilità di comandi primitivi, nella lezione odierna verranno delineati alcuni aspetti più avanzati nella programmazione di shell.

Verrà, infine, ripreso e completato l'argomento che riguarda l'esecuzione remota di comandi di shell tramite interfaccia CGI presentando una possibile implementazione di test degli script che non necessita l'accesso a pagine di server remoti ma può realizzarsi localmente grazie alle specifiche caratteristiche fornite dai browser.

GRAMMATICA DELLA SHELL

Come si è già detto più volte le variabili di shell si riferiscono a tipi di dati ben definiti, ossia gli interi e le stringhe e i descrittori di file. In realtà le variabili non sono strutturate secondo le convenzioni dei linguaggi di programmazione evoluti come C, PASCAL, MODULA, JAVA, ecc..., ma devono essere considerate non tipizzate. Ciò non toglie che nella shell bash si possano strutturare opportunamente le variabili laddove è necessario.

DEFINIZIONI DI TIPO

In effetti è possibile associare un tipo ad una variabile, grazie al comando di shell declare, cosicchè la dichiarazione

Si tenga presente, comunque, che la notazione di variabile nella shell prevede sempre l'impiego delle parentesi grafe, cosicchè quando varname si riferisce ad un valore assegnato, allora si deve scrivere

${varname}

le parentesi grafe potendosi trascurare solamente nel caso che non ci siano ambiguità interpretative da parte della shell.

DISABILITAZIONE DELL'ECO

In alcuni casi può risultare utile la disabiltazione dell'eco automatico dei caratteri introdotti da tastiera. Il comando stty serve allo scopo perchè in grado di modificare le proprietà dell'input. Si consideri, a questo proposito, l'esempio riportato di seguito


#!/bin/bash

function CheckPasswd() {
...........
return 0 ;
}

echo "The action you requested needs root privileges."
echo "Please enter root password to continue"
#
stty -echo    # Turns off screen echo.
echo -n "Password : "
read PASSWD
stty echo ;   # Restores screen echo.
if (CheckPasswd)
  then
    ...........
    ...........
fi
exit 0
Modifica dello stato di una periferica


Si noti l'impiego del comando read per leggere una linea di input, la cui sequenza di caratteri viene assegnata alla variabile PASSWD.

PASSAGGIO DEI PARAMETRI ALLA SHELL CHIAMANTE

Un altro aspetto interessante, di cui conviene tener conto quando si scrivono file di comandi per la shell, è l'assenza di uno schema generale di ritorno dei parametri assegnati dalla shell corrente rispetto alla shell chiamante. Infatti, poichè ciascun "shell script" è eseguito come un processo autonomo con proprie variabili locali, non esiste alcun modo di trasferire il valore di una variabile da una shell verso la shell chiamante, a meno che non si tratti delle cosiddette variabili d'ambiente che vengono ereditate da una shell all'altra a partire da quella di login che il sistema esegue dopo ogni autenticazione con successo dell'utente.

Per ovviare a questo inconveniente, tuttavia, è possibile far si che variabili e valori assocciati non vadano persi quando si ritorna alla shell chiamante facendo eseguire da quest'ultima il comando eval mentre la shell chiamata deve eseguire, per ogni variabile VARNAME a cui è associato il valore value, il comando echo "VARNAME=value" come nello schema riportato di seguito


#!/bin/bash
# action: shell script richiamato dal successivo shell script
#
..................
A="some_value"
..................
echo "TX=$A"
.................
exit 0



#!/bin/bash
# shell script che chiama il precedente e del quale vuole
# ereditare la variabile TX con il valore ad essa associato
#
............................
............................
export TX
eval `action`
#
# ora TX e' una variabile con valore "some value"
#
echo $TX
............................
............................
exit 0
Passaggio di parametri


Naturalmente, se necessario, lo script action va chiamato con i tutti i parametri richiesti per la sua esecuzione.

ESECUZIONE REMOTA DEI COMANDI

Si è visto nelle precedenti lezioni qual'è lo schema implementativo da utilizzarsi sul lato server affinchè questo possa eseguire le azioni richieste dal client. Naturalmente, per un corretto funzionamento del server è necessario che siano installati gli opportuni pacchetti che consentano al webserver di eseguire script interfacciati con CGI.

Per poter realizzare tali funzionalità, dunque, sarebbe necessario disporre di un server su cui poter caricare le pagine indicate. Una soluzione alternativa utile, specialmente in fase di test, è quella che utilizza solamente il client, simulando il trasferimento dei dati verso il server con un invio di mail, reso possibile dall'utilizzo di browser Netscape-compatibili.

Per poter effettivamente realizzare tale simulazione è necessario introdurre nel codice HTML, che deve essere eseguito dal browser, la sequenza di istruzioni riportate di seguito


<script language="JavaScript">
function Submit(theForm,nA) {
   var nArgs = theForm.length - nButtons
   var parms = ""
   var i = 0
   for (i = 0; i < nArgs; i++) {
      parms += theForm.elements[i].name + "=" +
             theForm.elements[i].value.replace(/\s/g,"+") + "&" }
   {parms += theForm.elements[nArgs].name + "=" +
          theForm.elements[nArgs].value}
   var TO = '<username>@localhost'
   var SUBJECT = 'subject=cgi-script'
   var MAILHEAD = '<TEXTAREA READONLY COLS=80 ROWS=1 NAME="QUERY_STRING">'
   var MAILFOOT = '</TEXTAREA>'
   var MAILTO = '"mailto:' + TO + '?' + SUBJECT + '"'
   document.writeln("<HTML>")
   document.writeln("<BODY>")
   document.writeln("<P>")
   document.writeln("<FORM ENCTYPE=text/plain ACTION=" + MAILTO + "METHOD=post>")
   document.writeln(MAILHEAD)
   document.writeln(parms)
   document.writeln(MAILFOOT)
   document.writeln("<P>")
   document.writeln("<INPUT TYPE=Submit VALUE=Submit><BR>")
   document.writeln("<INPUT TYPE=button NAME=Submit VALUE=html-view")
   document.writeln("onClick='window.open(\"answer.html\",\"Dest\")'<BR>")
   document.writeln("<INPUT TYPE=button NAME=Submit VALUE=text-view")
   document.writeln("onClick='window.open(\"answer.txt\",\"Dest\")'<BR>")
   document.writeln("</FORM>")
   document.writeln("</BODY>")
   document.writeln("</HTML>")
   document.close()
   return true
}
</script>
Raccolta dei parametri di una Form


il cui effetto è quello di generare una pagina dinamica che permette di vedere come vengono passati i parametri dal browser verso il server e che, in realtà, sono trasferiti alla mailbox locale dell'utente posta in /var/mail/<username>. Una volta letto dallo shell script CGI, il messaggio deve essere scaricato dalla mailbox con un qualunque programma di lettura della posta. Dal punto di vista del semplice rendering il risultato è analogo ai casi precedentemente visti.

Il secondo programma necessario per la simulazione di esecuzione remota è dal seguente shell script, che realizza l'estrazione dell'informazione contenuta nel messaggio, inviato dal browser Netscape con l'azione predefinita di mailto, e preparare l'esecuzione dello script CGI assegnando alle variabili d'ambiente gli stessi valori che il l'esecutore del webserver avrebbe assegnato al medesimo script.


#!/bin/bash
#  Synopsis: launch [-opt] cgi-script usermail

case "$#" in
0) echo "Usage: launch [-opt] cgi-script usermail";
   exit 2 ;;
1) echo "Insufficient arguments";
   exit 1 ;;
2) CGI_SCRIPT="$1";
   USERMAIL="/var/mail/$2";;
3) if [ `echo "$1" | grep -e "-"` ]; then > /dev/null
     OPT=`echo "$1" | sed 's/-//'`;
     CGI_SCRIPT="$2";
     USERMAIL="/var/mail/$3";
   else
     echo "Option required";
     exit 3
   fi ;;
esac

#TraceF=0;  do not uncomment this line

case $OPT in
d) TraceF=1 ;;
*) ;;
esac

ARGSFILE=`grep -e QUERY_STRING $USERMAIL | sed 's/^.\{1,12\}=//'`
WC=(`echo $ARGSFILE | wc -c`);
let "CONTENT_LENGTH = ${WC[0]} + 2";
REQUEST_METHOD=POST

export REQUEST_METHOD
export CONTENT_LENGTH

if [ "${TraceF:0}" ]; then
  echo "**********************************************************"
  echo "REQUEST_METHOD=$REQUEST_METHOD"
  echo "CONTENT_LENGTH=$CONTENT_LENGTH"
  echo "QUERY_STRING:<$ARGSFILE>"
  echo "**********************************************************"
fi

echo $ARGSFILE | ${CGI_SCRIPT}

RETVAL=$?
exit $RETVAL
Simulazione del CGI parse


Infine si modifichi la parte di pagina HTML che riguarda la sottomissione della FORM sostituendo il codice relativo a tali bottoni con il seguente

<INPUT TYPE="button" NAME=action VALUE="type of action" ONCLICK="Submit(this.form,K)">

dove K è l'ordine di apparizione del bottone considerato. Lo schema è puramente indicativo e va adattato alla propria configurazione di Linux. Ricordarsi di cancellare il messaggio di posta dopo che questo è stato utilizzato dallo script CGI.

L'ESEMPIO DELLA RUBRICA TELEFONICA RIVISITATA

A scopo esemplificativo si consideri nuovamente l'esempio discusso nella lezione precedente in cui veniva considerata l'implementazione di una rubrica telefonica accessibile remotamente via web. A parte il file che rappresenta il database degli indirizzi, generato e modificato automaticamente dalle procedure di add e delete, è necessario realizzare la pagina HTML consult.html e lo shell script consult.cgi con le considerazioni di cui si è appena detto.

Si vede, allora che per quanto riguarda la pagina HTML si ottiene la seguente struttura dove è necessario sostituire username con quello relativo al proprio "accounting"


<HTML>
<script language="JavaScript">
var nButtons = 4
</script>

<script src="../query.js">
</script>

<HEAD> <TITLE> Phone Book </TITLE></HEAD>
<BODY>
<H1> Phone Book </H1>
<HR>
<FORM NAME="phonebook" ACTION="consult.cgi" METHOD=POST>
<UL>
<LI>
Name : <INPUT TYPE="text" NAME=Name SIZE=20 MAXLENGTH=20>
Cognome : <INPUT TYPE="text" NAME=Surname SIZE=40 MAXLENGTH=40>
<LI>
Via : <INPUT TYPE="text" NAME=Street SIZE=25 MAXLENGTH=25>
Numero : <INPUT TYPE="text" NAME=Number SIZE=6 MAXLENGTH=6>
Città : <INPUT TYPE="text" NAME=City SIZE=20 MAXLENGTH=20>
<LI>
Prefix : <INPUT TYPE="text" NAME=Code SIZE=4 MAXLENGTH=4>
Phone : <INPUT TYPE="text" NAME=Phone SIZE=10 MAXLENGTH=10>
</UL>
<P>
<!--INPUT TYPE="submit" NAME=ACTION VALUE=add-->
<!--INPUT TYPE="submit" NAME=ACTION VALUE=delete-->
<!-- NPUT TYPE="submit" NAME=ACTION VALUE=search-->
<INPUT TYPE="button" NAME=action VALUE=add ONCLICK="Submit(this.form,1)">
<INPUT TYPE="button" NAME=action VALUE=delete ONCLICK="Submit(this.form,2)">
<INPUT TYPE="button" NAME=action VALUE=search ONCLICK="Submit(this.form,3)">
<INPUT TYPE="reset" VALUE=clear>
</FORM>
<HR>
</BODY>
</HTML>
Implementazione della rubrica telefonica


Per quanto riguarda lo shell script consult.cgi non è necessario fare alcuna modifica perchè il file cgiparse realizza già il corretto interfacciamento.

Per ottenere la simulazione bisogna, innanzitutto, lanciare il browser Netscape e caricare la pagina consult.html. Dopodichè si riempono i campi che appaiono nella FORM e si seleziona uno dei pulsanti. Sul browser appare una nuova pagina con i dati pronti ad essere trasferiti allo shell script. Selezionando "submit" viene attivato il "mailagent" del browser ed inviato un messaggio all'utente locale.

A questo punto, per lanciare lo shell script consult.cgi, è necessario eseguire lo script launch, riportato in precedenza, dove il primo parametro deve essere lo script CGI che si vuol eseguire mentre il secondo parametro è l'indirizzo di email locale dove sono stati spediti i valori dei parametri assegnati dalla form. In questo modo lo script andrà a leggere il contenuto del messaggio per trasformarlo nelle assegnazioni di valore alle variabili che appaiono nella FORM. Un eventuale messaggio potrebbe comparire come output nella finestra di shell utilizzata per lanciare lo script. Ridirigere l'output verso il file temporaneo answer.html in modo da poter essere visualizzato dal browser dopo aver selezionato uno dei bottoni di display html-view oppure text-view a seconda che il file prodotto si tipo html oppure semplice testo.