MANUALE UNIX - I FILES BATCH


I FILE BATCH


 

Un file batch in UNIX è un file testuale contenente una sequenza di richiami a comandi UNIX o programmi. L'obbiettivo dei files batch è l'automatizzazione di operazioni ripetitive.

Vediamo ora alcune istruzioni di base che ci consentiranno di costruire piccoli programmi. Occorre precisare che le diverse shell presenti in ambito UNIX implicano comandi differenti a secondo dell'interprete (shell) utilizzato. Nel presente documento verranno analizzate le shell sh/bash e csh.

 

LE VARIABILI E GLI ARRAY



Le variabili servono a registrare dei valori acquisiti durante l'esecuzione di un programma. L'inizializzazione di una variabile (assegnamento del valore iniziale) avviene, a seconda della shell utilizzata, in modo differente:
- mediante il comando SET se utilizziamo la shell csh  
- con un semplice assegnamento nel caso della shell sh

ESEMPIO VAR 1

 

Shell Sh - File setta.sh Shell csh - file setta.csh
# Inizializzazione
NOME="Marco"
# Stampa del valore
echo "Il mio NOME e': $NOME"
echo "Il mio nome e': $nome (le variabili sono CASE SENSITIVE)"
# Eliminazione della variabile
unset NOME
# Inizializzazione
set NOME="Marco"
# Stampa del valore
echo "Il mio NOME e': $NOME"
echo "Il mio nome e': $nome (le variabili sono CASE SENSITIVE)"
# Eliminazione della variabile
unset NOME


L'esecuzione dei due script produce i seguenti output:

Shell Sh - File setta.sh Shell csh - file setta.csh
sh-2.05# setta.sh
sh: setta.sh: command not found
sh-2.05# pwd
/home/studente
sh-2.05# echo $PATH
/usr/local/bin:/bin:/usr/bin:/home/studente/bin
sh-2.05# ./setta.sh
sh: ./setta.sh: Permission denied
sh-2.05# chmod 711 setta.sh
sh-2.05# setta.sh
sh: setta.sh: command not found
sh-2.05# ./setta.sh
Il mio NOME e': Marco
Il mio nome e':  (le variabili non sono CASE SENSITIVE)
sh-2.05# ./setta.csh
Il mio NOME e':
Il mio nome e':  (le variabili sono CASE SENSITIVE)
[studente@localhost studente]# setta.csh
setta.csh: Comando non trovato.
[studente@localhost studente]# pwd
/home/studente
[studente@localhost studente]# echo $PATH
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/bin:/home/studente/bin
[studente@localhost studente]# ./setta.csh
./setta.csh: Permission denied.
[studente@localhost studente]# chmod 711 setta.csh
[studente@localhost studente]# setta.csh
setta.csh: Comando non trovato.
[studente@localhost studente]# ./setta.csh
Il mio NOME e': Marco
nome: Variabile non definita.
[studente@localhost studente]# ./setta.sh
NOME=Marco: Comando non trovato.
NOME: Variabile non definita.

 

si osservi che:
- per eseguire un file batch occorre fornire i diritti di esecuzione necessari [ chmod 711 <nomefilebatch> ]
- se la cartella corrente non è nei percorsi registrati nella variabile d'ambiente
PATH è necessario anteporre ./  [cartella corrente] al batch da eseguire
- il nome delle variabili è case sensitive:
NOME è una variabile differente da nome.
- se eseguo lo script in una shell differente da quella utilizzata per la progettazione si potrebbero verificare degli errori [vedremo ora come risolvere questo ostacolo!]


Per eseguire uno script in una shell differente da quella usata nella progettazione del file batch è necessario inserire all'inizio del file i caratteri #! seguiti dal percorso della shell che deve interpretare i comandi ivi contenuti. Ecco gli esempi precedenti modificati a tale scopo

Shell Sh - File setta.sh Shell csh - file setta.csh
#!/bin/sh
# Inizializzazione
NOME="Marco"
# Stampa del valore
echo "Il mio nome e': $NOME"
echo "Il mio nome e': $nome (le variabili sono CASE SENSITIVE)"
# Eliminazione della variabile
unset NOME
#!/bin/csh -f
# Inizializzazione
set NOME="Marco"
# Stampa del valore
echo "Il mio nome e': $NOME"
echo "Il mio nome e': $nome (le variabili sono CASE SENSITIVE)"
# Eliminazione della variabile
unset NOME

I caratteri (#!), all'inizio dello script, informano il sistema che il file contiene una serie di comandi che devono essere eseguiti dall'interprete indicato dopo la sequenza #!. I caratteri #! rappresentano il magic number (due byte!) di un file batch unix. La sequenza #! identifica il tipo di file (in questo caso uno script di shell eseguibile!). Provate ad eseguite man magic per ottenere ulteriori dettagli in merito. Noterete che il magic number è utilizzato dal comando file per determinare il tipo di file. Immediatamente dopo #! compare un percorso: si tratta del percorso del programma che deve interpretare i comandi contenuti nello script. Questo programma può essere una shell, un linguaggio di programmazione o una utility (ad esempio awk). L'interprete quindi legge ed esegue i comandi dello script, partendo dall'inizio (la riga successiva a #!) ed ignorando gli eventuali commenti (#). Nella figura seguente vediamo l'esecuzione dei due batch scritti per shell differenti rispetto a quelle usate per l'esecuzione: come si nota funzionano!

Shell Sh - esecuzione di setta.csh e setta.sh Shell csh - esecuzione di setta.csh e setta.sh
sh-2.05# ./setta.sh
Il mio nome e': Marco
Il mio nome e':  (le variabili non sono CASE SENSITIVE)
sh-2.05# ./setta.csh
Il mio nome e': Marco
nome: Variabile non definita.
sh-2.05# file setta.sh
setta.sh: Bourne shell script text executable
sh-2.05# file setta.csh
setta.csh: C shell script text executable
[studente@localhost studente]# ./setta.sh
Il mio nome e': Marco
Il mio nome e':  (le variabili non sono CASE SENSITIVE)
[studente@localhost studente]# ./setta.csh
Il mio nome e': Marco
nome: Variabile non definita.
[studente@localhost studente]# file setta.sh
setta.sh: Bourne shell script text executable
[studente@localhost studente]# file setta.csh
setta.csh: C shell script text executable

In UNIX è possibile definire anche degli array (elenchi di valori recuperabili mediante un indice).
L'inizializzazione avviene con una scrittura differente a seconda della shell
- in sh gli array vengono definiti ad esempio in questo modo:
Colori=(rosso verde "blu di prussia" giallo magenta) . Il numero di elementi di un array può essere valutato utilizzando l'espressione ${#NomeArray[*]}
- in csh gli array vengono definiti in modo leggermente differente. L'inizializzazione precedente diventa: set Colori=(rosso verde "blu di prussia" giallo magenta). Il numero di elementi di un array può essere valutato utilizzando l'espressione $#NomeArray

ESEMPIO VAR 2

Vediamo ora un esempio di utilizzo degli array. Nell'esempio si utilizza il construtto iterativo while (ignoratelo per ora!) che analizzeremo più avanti.

Shell Sh - File array.sh Shell csh - file array.csh
#!/bin/sh
# Inizializzazione dell'array
Colori=(rosso "blu di prussia" giallo)
NrElementi=${#Colori[*]}
echo "Ha $NrElementi colori. Sono:"
i=0
while [ $i -lt $NrElementi ]; do
   echo "\$Colori[$i] = ${Colori[$i]}"
   let i++
done
# Aggiungo altri elementi in coda
Colori[${#Colori[*]}]="blu cobalto"
Colori[${#Colori[*]}]=Nero
NrElementi=${#Colori[*]}
echo "Dopo ha $NrElementi colori. Sono:"
let i=0
while [ $i -lt $NrElementi ]; do
   echo "\$Colori[$i] = $Colori[$i]"
   let i++
done
#!/bin/csh -f
# Inizializzazione array con valori senza spazi
echo "----------------- ARRAY NOMI ---------------"
set Nomi=(Mario Arturo Genoveffa)
set NrElementi=$#Nomi
echo "Ha $NrElementi Nomi. Sono:" $Nomi
# Aggiunta di un nuovo elemento
set Nomi=($Nomi "")
set Nomi[$#Nomi]="Amilcare"
set NrElementi=$#Nomi
echo "Ha $NrElementi Nomi. Sono:" $Nomi
# Inizializzazione array con valori con spazi
echo "--------------- ARRAY COLORI ---------------"
set Colori=(rosso "blu di prussia" giallo)
set NrElementi=$#Colori
echo "Ha $NrElementi colori. Sono:" $Colori
# Creazione stringa contenente i valori dell'array
# Ogni valore e' racchiuso da singolo apice '
set StrVal=""
@ i=1
while ($i <= $NrElementi)
   set StrVal="$StrVal""'$Colori[$i]' "
   @ i++
end
# Aggiunta di un nuovo elemento
set StrVal="$StrVal""'Blu cobalto' "
eval "set Colori = ($StrVal)"
set StrVal="$StrVal""Nero "
eval "set Colori = ($StrVal)"
set NrElementi=$#Colori
echo "Dopo ha $NrElementi colori. Sono:"
@ i=1
while ($i <= $NrElementi)
   echo \$"Colori[$i] = $Colori[$i]"
   @ i++
end
# Errore: Assegnamento out of range
@ i = $#Colori + 1
set Colori[$i]="Verde"


L'esecuzione dei due script produce i seguenti output:

Shell Sh - esecuzione di array.sh Shell csh - esecuzione di array.csh
sh-2.05# ./array.sh
Ha 3 colori. Sono:
$Colori[0] = rosso
$Colori[1] = blu di prussia
$Colori[2] = giallo
Dopo ha 5 colori. Sono:
$Colori[0] = rosso
$Colori[1] = blu di prussia
$Colori[2] = giallo
$Colori[3] = blu cobalto
$Colori[4] = Nero
[studente@localhost ~]$ ./array.csh
[root@localhost ~]# ./leggi.csh
----------------- ARRAY NOMI ---------------
Ha 3 Nomi. Sono: Mario Arturo Genoveffa
Ha 4 Nomi. Sono: Mario Arturo Genoveffa Amilcare
--------------- ARRAY COLORI ---------------
Ha 3 colori. Sono: rosso blu di prussia giallo
Dopo ha 5 colori. Sono:
$Colori[1] = rosso
$Colori[2] = blu di prussia
$Colori[3] = giallo
$Colori[4] = Blu cobalto
$Colori[5] = Nero
set: Indice fuori scala.


si osservi che:
- la numerazione degli elementi in sh parte da 0 mentre in csh parte da 1
- in sh/csh posso riferirmi al singolo elemento dell'array in questi due modi:
$Colori[$i] oppure ${Colori[$i]}
- in sh è possibile aggiungere nuovi elementi dinamicamente in modo molto semplice: Colori[${#Colori[*]}]="Nuovo colore"
- in csh gli array non si ridimensionano dinamicamente. Se provo ad assegnare un valore ad un elemento fuori dal range ricevo un errore {provate in una finestra csh a digitare la sequenza:
set x=(1 2 3);set x[4]=4. Vedrete a video un messaggio di errore del tipo "indice fuori scala"}. Per aggiungere un nuovo elemento occorre costruire una sequenza di istruzioni che ricopia tutti i valori contenuti nell'array in una stringa StrVal facendo attenzione a separare ogni valorizzazione con singoli apici (questo per evitare che gli spazi presenti in un valore siano interpretati come separatori dando così luogo a più valori). Infine con le istruzioni set StrVal="$StrVal""'Nuovo Valore' ";eval "set $Elenco=($strVal)" ricreo ex novo l'array accodando ai vecchi valori quello nuovo appena aggiunto. Quando l'array da ridimensionare non ha elementi (vedi parte iniziale dell'esempio array.csh con l'array nomi) con spazi nei valori posso effettuare il ridimensionamento con questa istruzione: set Elenco=($Elenco "").

OPERATORI NUMERICI



Per effettuare degli assegnamenti numerici con sh è possibile utilizzare il comando built-in let oppure l'operatore (()) Per la csh si usa l'operatore: @


ESEMPIO OPERATORI 1


Shell Sh - File operaz.sh Shell csh - File operaz.csh
let X=10
let Y=3
let Z=X+Y
echo $X + $Y = $Z
let Z=X-Y
echo $X - $Y = $Z
let Z=X*Y
echo "$X * $Y = $Z"
let Z=X**Y
echo $X ^ $Y = $Z
let Z=X/Y
echo $X : $Y = $Z
let Z=X%Y
echo $X MOD $Y = $Z
let A=8,3
let B=3,6
let C=A+B
echo ---------------------------------------------------
echo i comandi batch non gestiscono numeri decimali
echo ---------------------------------------------------
echo $A + $B = $C
echo ---------------------------------------------------
echo Se non delimito con \" posso avere degli errori se 
echo ho dei caratteri speciali nella stringa da stampare
echo ---------------------------------------------------
let C=A*B
echo $A * $B = $C
echo ---------------------------------------------------
echo Per i caratteri speciali posso usare lo \\ davanti 
echo al carattere speciale
echo ---------------------------------------------------
echo $A \* $B = $C
(( C = A ** B ))
echo $A ^ $B = $C
C=$(( A ** B ))
echo $A ^ $B = $C
(( A++, B-- ))
echo successivo A:$A - precedente B:$B
let "A = B + 1"
echo A=B+1=$A
#!/bin/csh -f
@ X = 10
@ Y = 3
@ Z = $X + $Y
echo $X + $Y = $Z
@ Z = $X - $Y
echo $X - $Y = $Z
@ Z = $X * $Y
echo "$X * $Y = $Z"
@ Z = $X / $Y
echo $X : $Y = $Z
@ Z = $X % $Y
echo $X MOD $Y = $Z
@ Z = $X ^ $Y
echo $X xor $Y = $Z
@ X++
echo "X++ = " $X
@ Y--
echo "Y-- = " $Y

 

Questi script producono rispettivamente i seguenti output:

 

Shell Sh - esecuzione di operaz.sh Shell csh - esecuzione di operaz.csh
sh-2.05# ./operaz.sh
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 ^ 3 = 1000
10 : 3 = 3
10 MOD 3 = 1
-----------------------------------------------
i comandi batch non gestiscono numeri decimali
-----------------------------------------------
8 + 3 = 11
---------------------------------------------------
Se non delimito con " posso avere degli errori se
ho dei caratteri speciali nella stringa da stampare
---------------------------------------------------
8 operaz 3 = 24
---------------------------------------------------
Per i caratteri speciali posso usare lo \ davanti
al carattere speciale
---------------------------------------------------
8 * 3 = 24
8 ^ 3 = 512
8 ^ 3 = 512
successivo A:9 - precedente B:2
A=B+1=3
[studente@localhost studente]# ./operaz.csh
10 + 3 = 13
10 - 3 = 7
10 * 3 = 30
10 : 3 = 3
10 MOD 3 = 1
10 xor 3 = 9
X++ =  11
Y-- =  2


Si osservi che nella shell sh:
- la divisione ammessa è quella intera
- non è possibile usare i decimali
- l'operatore ** (potenza) non è presente nelle prime shell sh
- la stringa
"$X * $Y = $Z" deve essere racchiusa tra doppi apici altrimenti viene visualizzato un errore dovuto alla presenza del carattere speciale *
- non posso inserire degli spazi nelle espressioni gestite con
let. In caso contrario devo racchiudere l'espressione tra doppi apici
- scrivere let x=x+1 è equivalente a let "x = x + 1"  o a  ((x = x +1)) o a x=$((x+1)) o a ((x=x+1)) o a  ((x++))

- all'interno dell'operatore (())le formule possono essere scritte utilizzando la stessa sintassi del C
- con l'operatore (()) posso eseguire più operazioni separandole con la virgola ((x++,y--)).

Si osservi, relativamente alla shell csh che:
- dopo
#!/bin/csh è possibile utilizzare le opzioni tipiche del comando csh: -v => effettua l'echo dei comandi presenti nello script -n => analizza la sintassi dello script ma non lo esegue -f => esegue la csh più velocemente poichè non esegue il file di configurazione $HOME/.cshrc.csh
-
non esiste l'operatore
** e ^ va inteso come xor bit a bit
-
MOD fornisce il resto della divisione intera
- dopo
@  è necessario inserire uno spazio prima del nome della variabile

 

Analizziamo ora gli operatori bit a bit presenti nella due shell analizzate:

 

Operatori bit a bit - bash, sh e csh
^ Bitwise XOR
| Bitwise OR
& Bitwise AND
~ Complemento a 1
<< Bitwise left shift
>> Bitwise right shift

 

ESEMPIO OPERATORI 2

ecco un esempio dove vengono utilizzati questi operatori

Shell Sh - File bitwise.sh Shell csh - File bitwise.csh
#!/bin/sh
let A=10
let B=7
echo "A=$A ; B=$B"
echo "In binario:                A = 00001010"
echo "                           B = 00000111"
let C=$A^$B
echo "C=A^B = $C =>      (A XOr B) = 00001101"
let C=($A | $B)
echo "C=A|B = $C =>       (A Or B) = 00001111"
let C=($A & $B)
echo "C=A&B = $C  =>      (A And B) = 00000010"
let C=~$A
echo "C=~A = $C => Bitwise Not(A) = 11110101"
echo "   è il complemento a uno di A"
echo "   11110101 è il complemento a 2 di -11"
let C=($A<<3)
echo "C=(A<<3) = $C => shift a sinistra di 3 bit"
echo "                    (A >> B) = 01010000"
let C=($A>>1)
echo "C=(A>>1) = $C => shift a destra di 1 bit"
echo "                    (A >> B) = 00000101"
#!/bin/csh -f
@ A=10
@ B=7
echo "A=$A ; B=$B"
echo "In binario:                A = 00001010"
echo "                           B = 00000111"
@ C = $A ^ $B
echo "C=A^B = $C =>      (A XOr B) = 00001101"  
@ C = ($A | $B)
echo "C=A|B = $C =>       (A Or B) = 00001111"  
@ C = ($A & $B)
echo "C=A&B = $C  =>      (A And B) = 00000010"  
@ C  = ~($A)
echo "C=~A = $C => Bitwise Not(A) = 11110101"
echo "   è il complemento a uno di A"
echo "   11110101 è il complemento a 2 di -11"
@ C = ($A<<3)
echo "C=(A<<3) = $C => shift a sinistra di 3 bit"
echo "                    (A >> B) = 01010000"  
@ C = ($A>>1)
echo "C=(A>>1) = $C => shift a destra di 1 bit"
echo "                    (A >> B) = 00000101"


L'esecuzione dei due script produce il medesimo output:

 

Shell csh/sh - esecuzione di operaz.csh/sh
[root@localhost ~]# ./bitwise.csh
A=10 ; B=7
In binario:                A = 00001010
                           B = 00000111
C=A^B = 13 =>      (A XOr B) = 00001101
C=A|B = 15 =>       (A Or B) = 00001111
C=A&B = 2  =>      (A And B) = 00000010
C=~A = -11 => Bitwise Not(A) = 11110101
   è il complemento a uno di A
   11110101 è il complemento a 2 di -11
C=(A<<3) = 80 => shift a sinistra di 3 bit
                    (A >> B) = 01010000
C=(A>>1) = 5 => shift a destra di 1 bit
                    (A >> B) = 00000101

 

si osserva:
- nella sh l'assegnamento non deve avere spazi a meno che non si utilizzino le () o i doppi apici: "
- per evitare incomprensioni con i simboli speciali << , >> & | si deve racchiudere l'espressione tra ()
- lo shift a destra (A >> n) sposta i bit della rappresentazione binaria di A a destra di n posizioni
- lo shift a sinistra (A << n) sposta i bit della rappresentazione binaria di A a sinistra di n posizioni
- La negazione a bit (complemento a 1) (~A) ribalta la rappresentazione binaria di A sostituendo gli uni con zero e viceversa

 

Analizziamo ora gli operatori di riassegnamento

 

Operatori di riassegnamento
*= A*=B A=A*B sh,csh
/= A/=B A=A/B sh,csh
+= A+=B A=A+B sh,csh
-= A-=B A=A-B sh,csh
%= A%=B A=A%B sh,csh
^= A^=B A=A^B sh,csh
&= A&=B A=A&B sh
|= A|=B A=A|B sh
<<= A<<=B A=A<<B sh
>>= A>>=B A=A>>B sh

 

ESEMPI OPERATORI 3

ecco un esempio in cui vengono utilizzati questi operatori

Shell Sh - File reassign.sh Shell csh - File reassign.csh
#!/bin/sh
let A=10
let B=7
echo "A=$A ; B=$B"
let A*=$B
echo "A*=B ovvero A=A*B => $A ; B=$B"
let A/=$B
echo "A/=B ovvero A=A:B => $A ; B=$B"
let A%=$B
echo "A%=B ovvero A=RESTO(A,B) => $A ; B=$B"
let A+=$B
echo "A+=B ovvero A=A+B => $A ; B=$B"
let A-=$B
echo "A-=B ovvero A=A-B => $A ; B=$B"
let A^=$B
echo "A^=B ovvero A=A^B => $A ; B=$B"
let "A &= $B"
echo "A&=B ovvero A=A&B => $A ; B=$B"
let $((A |= $B))
echo "A|=B ovvero A=A|B => $A ; B=$B"
let $((A<<=3))
echo "A<<=3 ovvero A=A<<3 => $A"
let "A>>=3"
echo "A>>=3 ovvero A=A<<3 => $A"
#!/bin/csh -f
@ A=10
@ B=7
echo "A=$A ; B=$B"
@ A*=$B
echo "A*=B ovvero A=A*B => $A ; B=$B"
@ A/=$B
echo "A/=B ovvero A=A:B => $A ; B=$B"
@ A %= $B
echo "A%=B ovvero A=RESTO(A,B) => $A ; B=$B"
@ A+=$B
echo "A+=B ovvero A=A+B => $A ; B=$B"
@ A-=$B
echo "A-=B ovvero A=A-B => $A ; B=$B"
@ A^=$B
echo "A^=B ovvero A=A^B => $A ; B=$B"


L'esecuzione dei due script produce (nella parte comune: testo in bianco) il medesimo output. In grigio l'output di reassign.sh.

 

Shell sh - esecuzione di reassign.sh
[root@localhost ~]# ./reassign.sh
A=10 ; B=7
A*=B ovvero A=A*B => 70 ; B=7
A/=B ovvero A=A:B => 10 ; B=7
A%=B ovvero A=RESTO(A,B) => 3 ; B=7
A+=B ovvero A=A+B => 10 ; B=7
A-=B ovvero A=A-B => 3 ; B=7
A^=B ovvero A=A^B => 4 ; B=7
A&=B ovvero A=A&B => 4 ; B=7
A|=B ovvero A=A|B => 7 ; B=7
A<<=3 ovvero A=A<<3 => 56
A>>=3 ovvero A=A<<3 => 7

PASSAGGIO DI PARAMETRI



Come si citava all'inizio di questo prontuario un batchfile è un file testuale nel quale sono inseriti dei comandi eseguibili. In unix è possibile progettare un batchfile che legge gli argomenti digitati direttamente sulla linea di comando (cioè scritti accanto al nome del file batch!).


ESEMPIO PARAMETRI 1

Si consideri un file batch Somma che effettua la somma degli argomenti passati sulla linea di comando. In altre parole quando digiterò ./Somma 1 23 12, l'esecuzione del nostro batch dovrà restituire la somma degli argomenti (vedi figura sottostante):

 

[studente@localhost studente]# ./Somma.sh 1 23 12
La somma 1 + 23 + 12 = 36
[studente@localhost studente]#


lo script somma (in sh e csh) che implementa l'esempio appena proposto (non viene gestito alcun controllo di errore!) è il seguente:


Shell Sh - File somma.sh Shell csh - File somma.csh
#!/bin/sh
if [ $# -eq 0 ]; then
   echo "Nessun argomento: somma impossibile!"
else
   let S=$1
   echo -n $1
   shift
   for c
   do
      echo -n " + $c"
      let S=S+c
   done
   echo " = $S"
fi
#!/bin/csh -f
@ S=0
if ($# == 0) then
   echo "Nessun argomento: somma impossibile!"
   goto Fine
endif
Inizio:
@ S = $S + $1
echo -n $1
shift
if ("$1" != "") then
   echo -n " + "
   goto Inizio
endif
echo " = $S"
Fine:


I parametri passati al batch sono leggibili tramite le variabili posizionali
$1 $2 ... $9, ${10}, ${11} ... ${!#}. A queste si aggiungono:
- $#: il numero di argomenti
-
$
0:
il nome del file batch file
- $*:
l'elenco completo dei parametri (in sh posso usare anche $@ ma non con la shell csh)
- ${!#}: nella shell sh è l'ultimo argomento passato.
- $argv[!#]: nella csh è l'ultimo argomento passato al file batch.


ESEMPIO PARAMETRI 2


Analizziamo l'esempio sottostante:

 

# Visualizzo i parametri
echo "Nome file batch           : $0"
echo "Tutti i parametri         : $*"
echo "Nr argomenti              : $#"
echo "primo parametro           : $1"
echo "secondo parametro         : $2"
echo "terzo parametro           : $3"
echo "quarto parametro          : $4"
echo "quinto parametro          : $5"
echo "sesto parametro           : $6"
echo "settimo parametro         : $7"
echo "ottavo parametro          : $8"
echo "nono parametro            : $9"
echo "decimo parametro          : ${10}"
echo "undicesimo parametro      : ${11}"
echo "dodicesimo parametro      : ${12}"
echo "tredicesimo parametro     : ${13}"
echo "quattordicesimo parametro : ${14}"
echo "quindicesimo parametro    : ${15}"
echo "...."
echo "ultimo argomento (sh)     : ${!#}"
#echo "ultimo argomento (csh)   : $argv[$#]"

 

la sua esecuzione produce:

 

sh-2.05# ./elenca A B C D E F G H I L M N O P Q R S T U V Z
Nome file batch           : ./elenca
Tutti i parametri         : A B C D E F G H I L M N O P Q R S T U V Z
Nr argomenti              : 21
primo parametro           : A
secondo parametro         : B
terzo parametro           : C
quarto parametro          : D
quinto parametro          : E
sesto parametro           : F
settimo parametro         : G
ottavo parametro          : H
nono parametro            : I
decimo parametro          : L
undicesimo parametro      : M
dodicesimo parametro      : N
tredicesimo parametro     : O
quattordicesimo parametro : P
quindicesimo parametro    : Q
....
ultimo argomento          : Z
sh-2.05# ./elenca A B
Nome file batch           : ./elenca
Tutti i parametri         : A B
Nr argomenti              : 2
primo parametro           : A
secondo parametro         : B
terzo parametro           :
quarto parametro          :
quinto parametro          :
sesto parametro           :
settimo parametro         :
ottavo parametro          :
nono parametro            :
decimo parametro          :
undicesimo parametro      :
dodicesimo parametro      :
tredicesimo parametro     :
quattordicesimo parametro :
quindicesimo parametro    :
....
ultimo argomento          : B

 

Relativamente a questo esempio possiamo osservare:
- i doppi apici " consentono la conservazione esatta degli spazi inseriti nelle frasi poste come argomento del comando echo
- se gli argomenti passati ad un batchfile sono in numero maggiore rispetto a quelli previsti la sequenza in eccesso viene persa
- se gli argomenti passati al batchfile sono in numero minore rispetto a quelli previsti le istruzioni predisposte per i parametri in eccesso vengono comunque eseguite. Per evitare l'inconveniente occorre utilizzare l'istruzione, che analizzeremo più avanti, IF  (ad esempio per evitare la stampa del 2° parametro si può scrivere: if [ $# -ge 2 ];then echo "\$2="$2;fi  oppure: if [ "$2" != "" ];then echo "\$2="$2;fi).

IL QUOTING


 

Per delimitare le stringhe si possono usare i caratteri di quoting singoli (’) o doppi (").
 

ESEMPIO QUOTING 1

lo scripting seguente

 

#!/bin/sh
# con csh mettere
#   set var=date
var=date
echo \$var  ===\> $var
echo \"\$var\" =\> "$var"
echo \'\$var\' =\> '$var'
echo \`\$var\` =\> `$var`
echo \$\{var\} =\> ${var}


produce questo output

[studente@localhost studente]# ./quoting.sh
$var ===> date
"$var" => date
'$var' => $var
`$var` => ven apr 30 01:21:43 CEST 2010
${var} => date


Si osserva:
- il testo delimitato dal carattere ` (backquote o tick - ascii 96) viene eseguito come se fosse un comando e al suo posto viene visualizzato l'output generato.
- il singolo apice ' (ascii 39) interpreta la stringa letteralmente come se fosse una sequenza di caratteri priva di qualsiasi significato particolare  (ad esempio non vengono rilevati e sostituiti i nomi delle  variabili presenti)
- Un backslash (\) prima di un carattere indica al sistema che deve considerare quel carattere letteralmente, senza assegnarli alcun significato speciale (esempio i metacaratteri).

ESEMPIO QUOTING 2

Il quoting può essere usato per separare una variabile da una stringa fissa. Ad esempio si consideri la variabile var, a cui è stato assegnato il valore "bat", e la stringa costante "man". Gli esempi successivi mostrano come combinare la variabile con la costante per ottenere come risultato la stringa "batman". Si noterà che l'istruzione echo $varman non funzionerà, poichè la shell tenta di valutare una variabile chiamata varman, che però non esiste. Per ottenere il risultato desiderato si deve separare opportunamente le stringhe, tramite il quoting oppure utilizzando le parentesi graffe {}. Ecco l'esempio utilizzando la shell sh:

 

#!/bin/sh
clear
# con csh mettere 
#   set var=bat 
var=bat
echo CORRETTI:
echo \"\$var\"man =\> "$var"man
echo \$var\"man\" =\> $var"man"
echo \$\{var\}man =\> ${var}man
echo \$var\\man   =\> $var\man
echo \$\{var\}man =\> ${var}man
echo \$var\"\"man =\> $var""man
echo ERRATI:
echo \\\$varman =\> \$varman
echo \`\$var\`man =\> `$var`man
echo \$var\`man\` =\> $var`man`
echo \{$\var\}man =\> {$var}man

che produce questo output

[studente@localhost studente]# ./quoting.sh
CORRETTI:
"$var"man => batman
$var"man" => batman
${var}man => batman
$var\man => batman
${var}man => batman
$var""man => batman
ERRATI:
\$varman => $varman
./quoting.sh: bat: command not found
`$var`man => man
Quale pagina di guida vuoi?
$var`man` => bat
{$var}man => {bat}man
[root@localhost studente]#  
[root@localhost studente]# man 
Quale pagina di guida vuoi?
[root@localhost studente]# bat
sh: bat: command not found

 

Si osservi:
- I caratteri ", , \, { e } funzionano da delimitatori poichè non sono caratteri validi per il nome di una variabile
- la stringa ’man’ viene eseguita come se fosse il richiamo al comando di help man e al suo posto viene visualizzato il suo output: "Quale pagina di guida vuoi ?"
- la variabile nella stringa ’$var’ viene sostituita con il suo valore bat ed in seguito eseguita come se fosse un comando. Non essendo bat un comando riconosciuto verrà visualizzato un messaggio di errore: "sh: bat: command not found"

Ecco i caratteri di quoting (windows-1252)

34 DOPPIO APICE "
39 SINGOLO APICE '
96 ACCENTO GRAVE (BACKQUOTE) `
145 APICE SINISTRO
146 APICE DESTRO
147 DOPPIO APICE SINISTRO
148 DOPPIO APICE SINISTRO
180 ACCENTO ACUTO ´

INPUT ED OUTPUT


 

 

Per quanto riguarda l'output abbiamo visto che il comando echo visualizza a video il testo passato come argomento. Il comando clear consente di pulire completamente il video. Non esiste in unix un comando equivalente all' @ECHO OFF del dos visto che le shell unix non effettuano di default l'echo dei comandi eseguiti in un batch file a meno che non si utilizzino particolari opzioni della shell. Ad esempio in sh per visualizzare i comandi eseguiti basta inserire come magic number #!/bin/sh -v mentre in csh #!/bin/csh -X.


L'input viene gestito in modo differente tra csh e sh: In sh esiste il comando built-in read che consente di acquisire ciò che viene digitato da tastiera. In csh per leggere da tastiera si utilizza il comando: set variabile=$<.

ESEMPIO INPUT 1

Immaginiamo di dover leggere una frase da tastiera e di visualizzarla subito dopo. Lo script leggi, nelle due shell in esame, potrebbe essere il seguente.

 

Shell Sh - File leggi.sh Shell csh - File leggi.csh
#!/bin/sh
# \c: sequenza di escape che impedisce 
# di andare a capo. Richiede l'opzione -e
# è equivalente a echo -n "Digita qualcosa: "
echo -e "Digita qualcosa: \c"
read INPUT
echo Hai digitato: $INPUT
#!/bin/csh -f
echo -n "Digita qualcosa ("\$\<" tra doppi apici): "
set INPUT="$<"
echo hai digitato $INPUT
echo -n "Digita qualcosa ("\$\<" senza doppi apici): "
set INPUT=$<
echo hai digitato $INPUT

l'esecuzione di questi script determina rispettivamente questi output

Shell Sh - esecuzione di leggi.sh Shell csh - esecuzione di leggi.csh
sh-2.05# ./leggi.sh
Digita qualcosa: AVE STUDENT
Hai digitato: AVE STUDENT
sh-2.05# ./leggi.sh
Digita qualcosa: AVE \STUDENT \
BUONO
Hai digitato: AVE STUDENT
[root@localhost ~]# ./leggi.csh
Digita qualcosa ($< tra doppi apici): AVE STUDENT
hai digitato AVE STUDENT
Digita qualcosa ($< senza doppi apici): AVE STUDENT
hai digitato AVE
[root@localhost ~]# ./leggi.csh
Digita qualcosa ($< tra doppi apici): AVE \
hai digitato AVE \
Digita qualcosa ($< senza doppi apici): AVE \
set: Il nome della variabile deve iniziare con una lettera.


Si osservi che:
- in sh inserendo uno \ in fondo alla linea è possibile leggere su più righe
- gli invii non vengono inseriti nella variabile utilizzata dall'istruzione read
- se con 'read' (in sh) utilizzo l'opzione -r lo \ finale viene interpretato come un carattere normale
- con una singola istruzione di read si possono impostare più variabili. Basta scrivere, dopo il comando read, l'elenco delle variabili da leggere. Se, durante l'esecuzione, si immettono meno valori (separati mediante spazio, tab o /) rispetto al numero di variabili predisposte, le rimanenti restano nulle. Se invece si immettono più valori rispetto alle variabili impostate allora l'ultima variabile acquisisce la parte restante fino all'invio (Ad esempio: se durante l'esecuzione dell'istruzione: read INPUT1 INPUT2 digito AVE STUDENTE MODELLO allora la variabile INPUT1 verrà valorizzata con la stringa AVE mentre INPUT2 con la parte restante: STUDENTE MODELLO).
- in csh se non metto i doppi apici "$<" non posso salvare in una sola variabile una frase contenente degli spazi (esempio AVE STUDENT).


ESEMPIO INPUT 2

Scriviamo il seguente script
leggi_anagrafica.sh
 

read -r -p "Digita Cognome Nome e Provincia: " COGNOME NOME PROVINCIA
echo Cognome   : $COGNOME
echo Nome      : $NOME
echo Provincia : $PROVINCIA


ed analizziamo la sua esecuzione sotto la shell sh
 

[studente@localhost 5i]# ./leggi_anagrafica.sh
Digita Cognome Nome e Provincia: Sechi Marco /
Cognome : Sechi
Nome : Marco
Provincia : /


Si osservi che l'opzione -p consente di visualizzare la frase che spiega la richiesta di input evitando così l'uso di echo iniziale

ESEMPIO INPUT 3

Opportune opzioni del comando read consentono funzionalità simili a quelle presenti nel comando choice del dos.


read -s -t2 -n1 -p "Premi un tasto entro 2 secondi ..." tasto
if [ ! -z "$tasto" ]; then
  echo; echo Hai premuto: $tasto
else
  echo; echo "Tempo scaduto."
fi


Si osservi:
- l'opzione -s evita la visualizzazione del carattere digitato
- l'opzione -t<secondi> indica il tempo entro il quale devo digitare qualcosa
- l'opzione -n<nr caratteri> indica quanti caratteri devo considerare nella lettura


ecco un esempio di esecuzione dello script appena analizzato


[studente@localhost 5i]# ./attesa
Premi un tasto entro 2 secondi ...
Tempo scaduto.
[studente@localhost 5i]# ./attesa
Premi un tasto entro 2 secondi ...
Hai premuto: a

 

Uno schema riassuntivo per il comando read è la seguente
 

Opzioni Significato
-a ANAME Le parole digitate vengono salvate sequenzialmente negli elementi dell'array ANAME che cresce in modo dinamico. L'indice dell'array parte da 0. Qualsiasi altra variabile passata al comando read verrà ignorata. Vediamo lo script d'esempio Leggi_Elenco.sh
 
read  -a ELENCO -p "Scrivi un elenco di nomi : "
let i=0
let N=${#ELENCO[*]}
while [ $i -lt $N ]
do
  echo $(($i+1)) Nominativo: ${ELENCO[$i]}
  let i=i+1
done

la cui esecuzione produce:
 
[root@localhost 5i]# ./Leggi_Elenco.sh
Scrivi un elenco di nomi : Paola Francesco Genoveffa
1 Nominativo: Paola
2 Nominativo: Francesco
3 Nominativo: Genoveffa
-d DELIM Questa opzione consente di terminare un input con un carattere differente dall'invio. Il delimitatore non verrà caricato nella variabile utilizzata dal comando read.

esempio

read  -d\; -p "Scrivi qualcosa (termina con ;) : " INPUT
echo; echo $INPUT
read -d, -p "Scrivi qualcosa (termina con ,) : " INPUT
echo; echo $INPUT

l'esecuzione di questo script produce:

[root@localhost 5i]# ./leggi_terminatore.sh
Scrivi qualcosa (termina con ;) : Ciao
sono
io;
Ciao sono io
Scrivi qualcosa (termina con ,) : Ciao sono io,
Ciao sono io
-r Raw mode. Con questa opzione il carattere \ (backslash) non indica che l'input continua sulla riga successiva (vedi esempio successivo opzione -e) ma viene interpretato come un carattere normale
-e Attivando questa opzione quando vado a capo con \ la domanda  viene ripetuta
 
read -p "Scrivi qualcosa (nessuna opzione): " INPUT
echo $INPUT
read -r -p "Scrivi qualcosa (opzione -r): " INPUT
echo $INPUT
read -e -p "Scrivi qualcosa (opzione -e) : " INPUT
echo $INPUT

l'esecuzione di questo script produce:

[root@localhost 5i]# ./prova_opzioni.sh
Scrivi qualcosa (nessuna opzione): prima \
seconda \
terza
prima seconda terza
Scrivi qualcosa (opzione -r): prima \
prima \
Scrivi qualcosa (opzione -e): prima \
Scrivi qualcosa (opzione -e): seconda \
Scrivi qualcosa (opzione -e): terza
prima seconda terza
-n NCHARS read termina dopo aver letto NCHARS caratteri oppure appena batto invio. La variabile associata al comando read verrà inizializzata con al massimo NCHARS caratteri
-p PROMPT Visualizza la frase di richiesta senza inserire un invio al termine.
-s Silent mode. L'input non viene visualizzato sul terminale
-tTIMEOUT Imposta un timeout entro il quale l'utente deve digitare qualcosa. Questa opzione non ha alcun effetto se l'istruzione read non sta leggendo dal terminale o da una pipe
-u FD Consente la lettura dal descrittore di file FD. - Attenzione! Tale opzione non è sempre presente in tutte le versioni sh. Lo script incolonna.sh sottostante ad esempio funziona nella Damn SmallLinux ma non in RedHat 7.2 (ambienti utilizzati durante le lezioni).
 
si considerino due files:
PAOLA.TXT  e MARCO.TXT
 
MARCO.TXT PAOLA.TXT
Nome: Marco
Alto: 1,72 mt
Peso: 78 kg
Capelli: Neri
Occhi: Marroni
 
Nome: Paola
Alto: 1,63 mt
Peso: 63 kg
Capelli: Rossi
Occhi: Verdi
Parentela: zia

e lo script incolonna.sh seguente:

while read -u3 f1 && read -u4 f2; do
    echo -e "$f1\t$f2"
done 3<$1 4<$2

la sua esecuzione produce questo output:
 
[root@localhost 5i]# incolonna.sh MARCO.TXT PAOLA.TXT
Nome: Marco	Nome: Paola	
Alto: 1,72 mt	Alto: 1,63 mt
Peso: 78 kg	Peso: 63 kg
Capelli: Neri	Capelli: Rossi
Occhi: Marroni	Occhi: Verdi

si osservi:
- l'istruzione 3<$1 redirige il contenuto del file indicato nel 1° parametro sul file identificato dal descrittore 3
- l'istruzione
4<$2 redirige il contenuto del file indicato nel 2° parametro sul file identificato dal descrittore 4
- l'istruzione
read -u3 f1 legge la riga corrente sul file con descrittore 3 e la carica nella variabile f1
- l'istruzione read -u4 f2 legge la riga corrente sul file con descrittore 4 e la carica nella variabile f2
- con l'istruzione
while il ciclo continua finché ho delle righe da leggere in entrambe i files (uso l'operatore logico &&). L'esecuzione si arresta quando arrivo in fondo ad uno dei due files
- l'
&& nel ciclo while assicura che la stampa avverrà  solo quando entrambe le righe sono state lette

IF ELSE - ISTRUZIONE DI SELEZIONE


 

 

Il construtto IF consente di replicare la nostra capacità selettiva ovvero "svolgo un'operazione basandomi su determinate condizioni".

In sh la sintassi della forma IF più compatta è la seguente:


if TEST-COMMANDS; then True-COMMANDS; else False-COMMANDS; fi


ecco un esempio di utilizzo direttamente sulla linea di comando

sh-2.05# echo $SHELL
/bin/bash
sh-2.05# if [ $SHELL == "/bin/bash" ]; then echo "Sei in una SH shell"; else echo "Sei nella shell: $SHELL"; fi
Sei in una SH shell
sh-2.05# if [ $SHELL == "/bin/csh" ]; then echo "Sei in una C-Shell"; else echo "Sei nella shell: $SHELL"; fi
Sei nella shell: /bin/bash

 

esiste una formulazione più estesa, che  riportiamo qui sotto, valida per le shell bash/sh:

 

if condizione1; then
    comandi da eseguire se la condizione1 è vera (true)
[elif condizione2; then
    comandi da eseguire se la condizione2 è vera (true)]
...
[elif condizioneN; then
    comandi da eseguire se la condizioneN è vera (true)]
...
[else
   comandi da eseguire se tutte le precedenti condizioni sono false (false)]
fi


L'equivalente per la shell csh è la seguente

if (condizione1) then
    comandi da eseguire se la condizione1 è vera (true)
[else if (condizione2) then
    comandi da eseguire se la condizione2 è vera (true)]
...
[else if (condizioneN) then
    comandi da eseguire se la condizioneN è vera (true)]
...
[else
   comandi da eseguire se tutte le precedenti condizioni sono false (false)]
endif


ESEMPIO IF 1

Il primo script di esempio consente di valutare se quello che digito è un numero e, in caso affermativo, di valutare se si tratta di un numero positivo, negativo oppure zero.

 

Shell sh - File if.sh Shell csh - file if.csh
#!/bin/sh
unset STRINGA
read -p "Digita un numero: " STRINGA
let NUMERO=$STRINGA
if [ $NUMERO == $STRINGA ]; then
   if [ $NUMERO -lt 0 ]; then
      echo "$NUMERO: numero negativo"
   elif [ $NUMERO -gt 0 ]; then
      echo "$NUMERO: numero positivo"
   else
      echo "$NUMERO: zero"
   fi
else
   echo "$STRINGA NON E' UN NUMERO"
fi 
#!/bin/csh -f
echo -n "Digita un numero: "
set STRINGA=$<
set NUMERO=`echo $STRINGA  | awk '/^[\-,0-9]+$/'`
if ($NUMERO == $STRINGA) then
   if ($NUMERO < 0) then
      echo $NUMERO\: numero negativo
   else if ($NUMERO > 0) then
      echo $NUMERO\: numero positivo
   else
      echo $NUMERO\: Zero
   endif
else
  echo "$STRINGA NON E' UN NUMERO"
endif


Si osservi:
- in sh l'istruzione [ espressione ] può essere sostituita con test espressione. Test espressione restituisce 0 se l'espressione è vera altrimenti 1 (falso)
- l'istruzione set NUMERO=`echo $STRINGA | awk '/^[\-,0-9]+$/'` sostituisce l'istruzione @ NUMERO = $STRINGA poichè quest'ultima visualizza un errore quando $STRINGA non contiene un numero (l'utility awk verrà illustrata in dettaglio in seguito).
- si noti l'uso di \: nello script csh. Infatti i : sono il simbolo speciale: modificatore di variabile. Ecco degli esempi:
  > esempio A sia set A="/bin/bash/pippo.txt" allora:
  A.1) [head] echo $A:h visualizza /bin/bash 
  A.2) [tail] echo $A:t visualizza pippo.txt
  A.3) [extension] echo $A:e visualizza txt 
  A.4) [root] echo $A:r visualizza /bin/bash/pippo
  > esempio B sia set A=`ls` allora:
  B.1) [all extension] echo $A:ge visualizza l' elenco di tutte le estensioni dei files della cartella corrente.
 Esistono altri modificatori come gh, gr e gt)

 

Vediamo l'output prodotto dall'esecuzione di questi script

 

Shell sh - esecuzione di if.sh Shell csh - esecuzione di if.csh
sh-2.05$ ./if.sh
Digita un numero: 32
32: numero positivo
sh-2.05$ ./if.sh
Digita un numero: -21
-21: numero negativo
sh-2.05$ ./if.sh
Digita un numero: 0
0: zero
sh-2.05$ ./if.sh
Digita un numero: ca
ca NON E' UN NUMERO
[studente@localhost ~]$ ./if.csh
Digita un numero: 21
21: numero positivo
[studente@localhost ~]$ ./if.csh
Digita un numero: -21
-21: numero negativo
[studente@localhost ~]$ ./if.csh
Digita un numero: 0
0: Zero
[studente@localhost ~]$ ./if.csh
Digita un numero: ca
ca NON E' UN NUMERO

 
Le condizioni utilizzate nell'IF sono costruite utilizzando gli OPERATORI DI CONFRONTO. A seconda della shell e del tipo di dati questi operatori variano.

Vediamo quindi gli operatori di confronto utilizzati per le stringhe.
 

Operatori di confronto stringhe - bash, sh
str1 = str2 uguale
str1 != str2 diverso
str1 \< str2 minore
str1 \> str2  maggiore 
-n str vera se str non vuota
-z str vera se str è "" ovvero se la lunghezza in caratteri è zero
str vera se str non è nulla
Operatori di confronto stringhe - csh
=~ contiene
!~ non contiene
str1 == str2 uguale
str1 != str2 diverso
{comando} vera se comando termina con un valore di uscita (exit status) uguale a 0

 

ESEMPIO IF 2

Ecco alcuni esempi di utilizzo di questi operatori di confronto in sh e csh

Shell sh - File str_relaz.sh Shell csh - file str_relaz.csh
#!/bin/sh
set -x +v
echo "---------------------------------------------------------"
echo "Se il batch e' corretto devono apparire solo "
echo "AFFERMAZIONI VERE"
echo "---------------------------------------------------------"
A=
# "$A" vera se $A  e' non nulla (stringa vuota)
if [ "$A" ]; then
   echo " 1.1) AFFERMAZIONE FALSA:  \$A e' non nulla"
fi
# -z "$A" vera se $A se ha zero caratteri
if [ -z "$A" ]; then
   echo " 2.1) AFFERMAZIONE VERA:  \$A ha lunghezza zero {verifica: "${#A}"}"
fi
A="Studiare Info e' bellissimo"
# "$A" vera se $A  e' non nulla (stringa vuota)
if [ "$A" ]; then
   echo " 3.2) AFFERMAZIONE VERA : \$A e' non nulla"
fi
# -z "$A" vera se $A se ha zero caratteri
if [ -z "$A" ]; then
   echo " 4.2) AFFERMAZIONE FALSA: \$A ha lunghezza zero {verifica: "${#A}"}"
fi
if [ -n "$A" ]; then
   echo " 5.3) AFFERMAZIONE VERA:  \$A ha "${#A}" caratteri"
fi
if [ "$A" == "Studiare Info e' bellissimo" ]; then
   echo " 6.4) AFFERMAZIONE VERA:  \$A e' uguale alla frase"
fi
if [ "$A" != "Studiare Info e' bellissimo" ]; then
   echo " 7.3) AFFERMAZIONE FALSA: \$A e' diverso"
fi
if [ "$A" != "STUDIARE Info e' bellissimo" ]; then
   echo " 8.5) AFFERMAZIONE VERA:  \$A e' diverso"
fi
if [ "ABACO" \< "ZORRO" ]; then
   echo " 9.6) AFFERMAZIONE VERA:  ABACO < ZORRO"
fi
if [ "ZORRO" \> "ABACO" ]; then
   echo "10.7) AFFERMAZIONE VERA:  ZORRO > ABACO"
fi
if [ "6" \> "57" ]; then
   echo "10.7) AFFERMAZIONE VERA:  '6' > '57'"
fi
if [ 6 -lt 57 ]; then
   echo "10.7) AFFERMAZIONE VERA:  6 < 57"
fi 
#!/bin/csh -f
echo "---------------------------------------------------------"
echo "Se il batch è corretto devono apparire solo "
echo "AFFERMAZIONI VERE"
echo "---------------------------------------------------------"
set A=   # otterrei lo stesso risultato con set A=""
# "$A" vera se $A  e' non nulla (stringa vuota)
if ("$A" == "") then
   echo " 1.1) AFFERMAZIONE VERA:  "\$"A è la stringa vuota"
endif
if ("$A" != "") then
   echo " 2.1) AFFERMAZIONE FALSA: "\$"A non è la stringa vuota"
endif
set A="Studiare Info è bellissimo"
if ("$A" == "") then
   echo " 3.2) AFFERMAZIONE FALSA: "\$"A è la stringa vuota"
endif
if ("$A" != "") then
   echo " 4.2) AFFERMAZIONE VERA:  "\$"A non è la stringa vuota"
endif
if ( "$A" == "Studiare Info è bellissimo" ) then
   echo " 5.3) AFFERMAZIONE VERA:  "\$"A è uguale alla frase"
endif
if ( "$A" == "STUDIARE Info è bellissimo" ) then
   echo " 6.3) AFFERMAZIONE FALSA: "\$"A è uguale alla frase"
endif
# "$A" =~ "Str" vera se $A contiene Str
if ( "$A" =~ *Anfo* ) then
   echo " 7.4) AFFERMAZIONE FALSA: "\$"A contiene Anfo"
endif
if ( "$A" =~ *Info* ) then
   echo " 8.4) AFFERMAZIONE VERA:  "\$"A contiene Info"
endif
# "$A" !~ "Str" vera se $A non contiene Str
if ( "$A" !~ *Anfo* ) then
   echo " 9.5) AFFERMAZIONE VERA:  "\$"A non contiene Anfo"
endif
if ( "$A" !~ *Info* ) then
   echo "10.5) AFFERMAZIONE FALSA: "\$"A non contiene Info"
endif


Si osservi che:
- se il valore della variabile contiene degli spazi è necessario, nella condizione di test, racchiudere la variabile tra doppi apici. Infatti la sostituzione della variabile con il suo valore determina nell'istruzione di test la creazione di tanti argomenti quante sono le parole separate dallo spazio e questo comporta la visualizzazione del seguente errore:  "if: Errore di sintassi nell'espressione"
-
in sh l'istruzione set -x impone la visualizzazione del valore contenuto nelle variabili mentre set -v la visualizzazione (echo) delle istruzioni eseguite nel batch. In alternativa potevamo innserire queste opzioni direttamente nel magic number #!/bin/sh -x -v.
- Si ricordi che il confronto tra stringhe è differente da quello numerico. Ad esempio la condizione [ "6" \> "57" ] è vera mentre [ 6 -gt 57 ] è falsa.
- Gli operatori <, > si basano sul confronto lessicografico
- gli operatori
=~ , !~ non vogliono i "


Vediamo ora alcuni degli operatori di test sui files con indicato a fianco le shell che li supportano

 

Operatori di test sui file - bash, sh
-e file vero se file esiste sh,csh
-f file vero se file è un file ordinario e non una cartella ne una device sh,csh
-d file vero se file è una cartella sh,csh
-c file vero se file è un device a caratteri sh,csh
-b file vero se file è un dispositivo a blocchi sh,csh
-p file vero se file è una named pipe sh,csh
-S file vero se file è un socket sh,csh
-k file vero se file ha il flag "sticky bit" (appiccicoso)attivo. [Un file con questo flag attivo, quando è eseguito viene mantenuto in memoria per consentire un avvio più rapido nelle riesecuzioni successive. Per le cartelle lo sticky bit assume un significato diverso: se un utente è in una cartella non sua ed ha i diritti di scrittura non potrà eliminare alcun file. Lo "Sticky bit" viene attivato con il comando chmod 1xxx nomefile oppure chmod +t nomefile. Al posto degli xxx useremo la tripla di numeri ottenuta come somma dei valori 0 (-) 1 (x) - 2 (w) e 4 (r) sh,csh
-u file vero se file ha il flag "set user-id" (suid) attivo. [Quando un file eseguibile, appartenente all'utente root, ha questo flag attivo verrà eseguito con i previlegi di amministratore anche se richiamato da un non superutente.] Lo "suid" flag è attivato con il comando chmod 4xxx nomefile oppure chmod u+s nomefile sh,csh
-g file vero se file ha il flag "set group-id" (sgid) attivo. [Quando una directory ha il flag "sgid" attivo allora un file creato al suo interno avrà come gruppo proprietario quello della cartella e non quello principale dell'utente che ha effettuato l'operazione]. Questa modalità risulta utile nelle cartelle condivise da un gruppo di utenti. Lo "sgid" flag è attivato con il comando chmod 2xxx nomefile oppure chmod g+s nomefile sh,csh
-t FileDescriptor vero se file descriptor è associato ad un terminale sh,csh
-r file
-w file
-x file
vero se file è a sola lettura per il proprietario
vero se file è scrivibile per il proprietario
vero se file è eseguibile per il proprietario
sh,csh
file1 -ot file2 vero se il file1 è più vecchio del file2 sh
file1 -ef file2 vero se il file1 e il file2 sono link allo stesso file sh
file1 -nt file2 vero se il file1 è più recente del file2 sh
-O file vero se l'utente collegato è il proprietario (owner) di file sh
-G file vero se il gruppo proprietario di file è uno dei gruppi a cui appartiene l'utente connesso. sh
-h o -L file vero se file è un link simbolico sh
-N file vero se file è stato modificato dopo l'ultima lettura Non attivo in sh e csh di redhat 7.2
-z file vero se file esiste ed ha dimensione uguale a zero csh
-s file vero se file esiste ed ha dimensione maggiore di zero sh,csh

 

ESEMPIO IF 3.A

ecco un esempio di utilizzo degli operatori di test sui files in sh

Shell sh - File file_oper.sh
echo -------------------------------------------
echo TEST sulla cartella /bin
echo -------------------------------------------
if [ -e /bin/ls ]; then
   echo "A-1.1) AFFERMAZIONE VERA: /bin/ls esiste come file"
fi
if test -e /bin/sechi; then
   echo "A-2.1) AFFERMAZIONE FALSA: /bin/sechi esiste come file"
fi
if test -d /bin; then
   echo "A-3.2) AFFERMAZIONE VERA: /bin e' una cartella"
fi
if [ -d /bin/ls ]; then
   echo "A-4.2) AFFERMAZIONE FALSA: /bin/ls e' una cartella"
fi
if [ -h /bin/sh ]; then
   echo "A-5.3) AFFERMAZIONE VERA: /bin/sh è un link simbolico"
fi
if [ -f /bin/ls ]; then
   echo "A-6.4) AFFERMAZIONE VERA: /bin/ls è un file ordinario (plain file)"
fi
if [ -s /bin/ls ]; then
   echo "A-7.5) AFFERMAZIONE VERA: /bin/ls esiste ed ha dimensione maggiore di 0"
fi
# sh e bash2 sono dei link a /bin/bash
if [ /bin/sh -ef /bin/bash2 ]; then
   echo "A-8.6) AFFERMAZIONE VERA: /bin/sh e /bin/bash2 sono link allo stesso file"
fi
# /bin/ps è del 28 agosto 2001 - /bin/ls è del 9 agosto 2001
if [ /bin/ps -ot /bin/ls ]; then
   echo "A 9.3) AFFERMAZIONE FALSA: /bin/ps  e' piu' vecchio di /bin/ls"
fi
if [ /bin/ps -nt /bin/ls ]; then
   echo "A10.7) AFFERMAZIONE VERA: /bin/ps  e' piu' recente di /bin/ls"
fi
echo -------------------------------------------
echo TEST sui file /dev
echo -------------------------------------------
if [ -b /dev/cdrom ]; then
   echo "B-1.1) AFFERMAZIONE VERA: /dev/cdrom è una device a blocchi"
fi
if [ -c /dev/tty ]; then
   echo "B-2.2) AFFERMAZIONE VERA: /dev/tty è una device a caratteri"
fi
if [ -f /dev/tty ]; then
   echo "B-3.1) AFFERMAZIONE FALSA: /dev/tty è un file ordinario (plain file)"
fi
# /dev/initctl ha dimensione 0 ed è una named pipe
if [ -p /dev/initctl ]; then
   echo "B-4.3) AFFERMAZIONE VERA: /dev/initctl e' una pipe FIFO"
fi
echo -------------------------------------------
echo TEST sui file /etc/httpd/conf/httpd.conf
echo "Diritti httpd.conf: " `ls -al /etc/httpd/conf/httpd.conf|cut -c 1-10`
echo -------------------------------------------
# attributi /etc/httpd/conf/httpd.conf: -rw-r--r--
if [ -r /etc/httpd/conf/httpd.conf ]; then
   echo "C-1.1) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'"
   echo "                          leggibile (per il proprietario)"
fi
if [ -w /etc/httpd/conf/httpd.conf ]; then
   echo "C-2.2) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'"
   echo "                          scrivibile (per il proprietario)"
fi
if [ -x /etc/httpd/conf/httpd.conf ]; then
   echo "C-3.1) AFFERMAZIONE FALSA: /etc/httpd/conf/httpd.conf e'"
   echo "                          eseguibile (per il proprietario)"
fi
if [ -O /etc/httpd/conf/httpd.conf ]; then
   echo "C-4.3) AFFERMAZIONE VERA: l'utente collegato è proprietario"
   echo "                          di /etc/httpd/conf/httpd.conf"
fi
if [ -G /etc/httpd/conf/httpd.conf ]; then
   echo "C-5.4) AFFERMAZIONE VERA: un gruppo dell'utente collegato è "
   echo "                          proprietario di /etc/httpd/conf/httpd.conf"
fi
echo -------------------------------------------
echo TEST su questo file batch: file.sh
echo -------------------------------------------
# l'utente collegato è root ma il proprietario
# è studente
if [ -O /home/studente/file.sh ]; then
   echo "D-1.1) AFFERMAZIONE FALSA: l'utente collegato è "
   echo "                           proprietario di /home/studente/file.sh"
fi
chgrp root /home/studente/file.sh
if [ -G /home/studente/file.sh ]; then
   echo "D-2.1) AFFERMAZIONE VERA: un gruppo dell'utente collegato è"
   echo "                          proprietario di /home/studente/file.sh"
fi
chmod 0000 /home/studente/file.sh
echo "Diritti con 0000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if [ -k /home/studente/file.sh ]; then
   echo "D-3.2) AFFERMAZIONE FALSA: /home/studente/file.sh ha settato lo sticky bit"
fi
if [ -g /home/studente/file.sh ]; then
   echo "D-4.3) AFFERMAZIONE FALSA: /home/studente/file.sh ha settato lo sgid bit"
fi
if [ -u /home/studente/file.sh ]; then
   echo "D-5.4) AFFERMAZIONE FALSA: /home/studente/file.sh ha settato lo suid bit"
fi
chmod 1000 /home/studente/file.sh
echo "Diritti con 1000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if [ -k /home/studente/file.sh ]; then
   echo "D-6.2) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sticky bit"
fi
chmod 2000 /home/studente/file.sh
echo "Diritti con 2000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if [ -g /home/studente/file.sh ]; then
   echo "D-7.3) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sgid bit"
fi
chmod 4000 /home/studente/file.sh
echo "Diritti con 4000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if [ -u /home/studente/file.sh ]; then
   echo "D-8.4) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo suid bit"
fi
echo -------------------------------------------
echo TEST sui files descriptor
echo -------------------------------------------
if [ -t 0 ]; then
   echo "E-1) Questo messaggio non si vede se scrivo: file_oper.sh < input.txt"
fi
if [ -t 1 ]; then
   echo "E-2) Questo messaggio non si vede se scrivo: file_oper.sh > output.txt"
fi
if [ -t 2 ]; then
   echo "E-3) Questo messaggio non si vede se scrivo: file_oper.sh 2> stderr.txt"
fi
echo -------------------------------------------
echo TEST vari
echo -------------------------------------------
# Azzero il file
cat /dev/null > /home/studente/ciao
# ----------------------------------------------
# nella shell di RedHat 7.2 questo Test non
# è supportato: if [ -z /home/studente/ciao ]; then
# possiamo usare il negato del test -s
# ----------------------------------------------
if [ ! -s /home/studente/ciao ]; then
   echo "F-3.3) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione uguale a 0"
fi
echo "Accodo questa frase" >> /home/studente/ciao
if [ -s /home/studente/ciao ]; then
   echo "F-4.4) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione maggiore di 0"
fi

 

ESEMPIO IF 3.B

ed ecco l'esempio di utilizzo degli operatori di test sui files in csh

Shell csh - file str_relaz.csh
#!/bin/csh
echo "-------------------------------------------"
echo "TEST sulla cartella /bin"
echo "-------------------------------------------"
if ( -e /bin/ls ) then
   echo "A-1.1) AFFERMAZIONE VERA: /bin/ls esiste come file"
endif
if ( -e /bin/sechi ) then
   echo "A-2.1) AFFERMAZIONE FALSA: /bin/sechi esiste come file"
endif
if ( -d /bin ) then
   echo "A-3.2) AFFERMAZIONE VERA: /bin e' una cartella"
endif
if ( -d /bin/ls ) then
   echo "A-4.2) AFFERMAZIONE FALSA: /bin/ls e' una cartella"
endif
if ( -f /bin/ls ) then
   echo "A-5.3) AFFERMAZIONE VERA: /bin/ls è un file ordinario (plain file)"
endif
if ( -s /bin/ls ) then
   echo "A-6.4) AFFERMAZIONE VERA: /bin/ls esiste ed ha dimensione maggiore di 0"
endif
echo -------------------------------------------
echo TEST sui file /dev
echo -------------------------------------------
if ( -b /dev/cdrom ) then
   echo "B-1.1) AFFERMAZIONE VERA: /dev/cdrom è una device a blocchi"
endif
if ( -c /dev/tty ) then
   echo "B-2.2) AFFERMAZIONE VERA: /dev/tty è una device a caratteri"
endif
if ( -f /dev/tty ) then
   echo "B-3.1) AFFERMAZIONE FALSA: /dev/tty è un file ordinario (plain file)"
endif
# /dev/initctl ha dimensione 0 ed è una named pipe
if ( -p /dev/initctl ) then
   echo "B-4.3) AFFERMAZIONE VERA: /dev/initctl e' una pipe FIFO"
endif
echo -------------------------------------------
echo TEST sui file /etc/httpd/conf/httpd.conf
echo "Diritti httpd.conf: " `ls -al /etc/httpd/conf/httpd.conf|cut -c 1-10`
echo -------------------------------------------
# attributi /etc/httpd/conf/httpd.conf: -rw-r--r--
if ( -r /etc/httpd/conf/httpd.conf ) then
   echo "C-1.1) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'"
   echo "                          leggibile (per il proprietario)"
endif
if ( -w /etc/httpd/conf/httpd.conf ) then
   echo "C-2.2) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'"
   echo "                          scrivibile (per il proprietario)"
endif
if ( -x /etc/httpd/conf/httpd.conf ) then
   echo "C-3.1) AFFERMAZIONE FALSA: /etc/httpd/conf/httpd.conf e'"
   echo "                          eseguibile (per il proprietario)"
endif
echo -------------------------------------------
echo TEST su questo file batch: file.sh
echo -------------------------------------------
# l'utente collegato è root ma il proprietario
# è studente
chmod 0000 /home/studente/file.sh
echo "Diritti con 0000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if ( -k /home/studente/file.sh ) then
   echo "D-1.1) AFFERMAZIONE FALSA: /home/studente/file.sh ha settato lo sticky bit"
endif
if ( -g /home/studente/file.sh ) then
   echo "D-2.2) AFFERMAZIONE FALSA: /home/studente/file.sh ha settato lo sgid bit"
endif
if ( -u /home/studente/file.sh ) then
   echo "D-3.3) AFFERMAZIONE FALSA: /home/studente/file.sh ha settato lo suid bit"
endif
chmod 1000 /home/studente/file.sh
echo "Diritti con 1000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if ( -k /home/studente/file.sh ) then
   echo "D-4.1) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sticky bit"
endif
chmod 2000 /home/studente/file.sh
echo "Diritti con 2000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if ( -g /home/studente/file.sh ) then
   echo "D-5.2) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sgid bit"
endif
chmod 4000 /home/studente/file.sh
echo "Diritti con 4000 di /home/studente/file.sh: " `ls -al /home/studente/file.sh|cut -c 1-10`
if ( -u /home/studente/file.sh ) then
   echo "D-6.3) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo suid bit"
endif
echo -------------------------------------------
echo TEST sui files descriptor
echo -------------------------------------------
if ( -t 0 ) then
   echo "E-1) Questo messaggio non si vede se scrivo: file_oper.csh < input.txt"
endif
if ( -t 1 ) then
   echo "E-2) Questo messaggio non si vede se scrivo:"
   echo "                      file_oper.csh >& stderr.txt"
   echo "                      file_oper.csh > output.txt"
endif
if ( -t 2 ) then
   echo "E-3) Questo messaggio non si vede se scrivo:"
   echo "                      file_oper.csh >& stderr.txt"
endif
echo -------------------------------------------
echo TEST vari
echo -------------------------------------------
# Azzero il file
cat /dev/null > /home/studente/ciao
if ( -z /home/studente/ciao ) then
   echo "F-1.1) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione uguale a 0"
endif
if ( -s /home/studente/ciao ) then
   echo "F-2.1) AFFERMAZIONE FALSA: /home/studente/ciao esiste e ha dimensione uguale a 0"
endif
echo "Accodo questa frase" >> /home/studente/ciao
if ( -z /home/studente/ciao ) then
   echo "F-3.2) AFFERMAZIONE FALSA: /home/studente/ciao esiste e ha dimensione maggiore di 0"
endif
if ( -s /home/studente/ciao ) then
   echo "F-4.2) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione maggiore di 0"
endif


l'esecuzione di questi script produce i seguenti output

Shell Sh - esecuzione di if.sh Shell csh - esecuzione di if.csh
sh-2.05# ./file_test.sh
-------------------------------------------
TEST sulla cartella /bin
-------------------------------------------
A-1.1) AFFERMAZIONE VERA: /bin/ls esiste come file
A-3.2) AFFERMAZIONE VERA: /bin e' una cartella
A-5.3) AFFERMAZIONE VERA: /bin/sh è un link simbolico
A-6.4) AFFERMAZIONE VERA: /bin/ls è un file ordinario (plain file)
A-7.5) AFFERMAZIONE VERA: /bin/ls esiste ed ha dimensione maggiore di 0
A-8.6) AFFERMAZIONE VERA: /bin/sh e /bin/bash2 sono link allo stesso file
A10.7) AFFERMAZIONE VERA: /bin/ps  e' piu' recente di /bin/ls
-------------------------------------------
TEST sui file /dev
-------------------------------------------
B-1.1) AFFERMAZIONE VERA: /dev/cdrom è una device a blocchi
B-2.2) AFFERMAZIONE VERA: /dev/tty è una device a caratteri
B-4.3) AFFERMAZIONE VERA: /dev/initctl e' una pipe FIFO
-------------------------------------------
TEST sui file /etc/httpd/conf/httpd.conf
Diritti httpd.conf:  -rw-r--r--
-------------------------------------------
C-1.1) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'
                          leggibile (per il proprietario)
C-2.2) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'
                          scrivibile (per il proprietario)
C-4.3) AFFERMAZIONE VERA: l'utente collegato è proprietario
                          di /etc/httpd/conf/httpd.conf
C-5.4) AFFERMAZIONE VERA: un gruppo dell'utente collegato è
                          proprietario di /etc/httpd/conf/httpd.conf
-------------------------------------------
TEST su questo file batch: file.sh
-------------------------------------------
D-2.1) AFFERMAZIONE VERA: un gruppo dell'utente collegato è
                          proprietario di /home/studente/file.sh
Diritti con 0000 di /home/studente/file.sh:  ----------
Diritti con 1000 di /home/studente/file.sh:  ---------T
D-6.2) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sticky bit
Diritti con 2000 di /home/studente/file.sh:  ------S---
D-7.3) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sgid bit
Diritti con 4000 di /home/studente/file.sh:  ---S------
D-8.4) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo suid bit
-------------------------------------------
TEST sui files descriptor
-------------------------------------------
E-1) Questo messaggio non si vede se scrivo: file_oper.sh < input.txt
E-2) Questo messaggio non si vede se scrivo: file_oper.sh > output.txt
E-3) Questo messaggio non si vede se scrivo: file_oper.sh 2> stderr.txt
-------------------------------------------
TEST vari
-------------------------------------------
F-3.3) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione uguale a 0
F-4.4) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione maggiore di 0
[root@localhost ~]# ./file_test.csh
-------------------------------------------
TEST sulla cartella /bin
-------------------------------------------
A-1.1) AFFERMAZIONE VERA: /bin/ls esiste come file
A-3.2) AFFERMAZIONE VERA: /bin e' una cartella
A-5.3) AFFERMAZIONE VERA: /bin/ls è un file ordinario (plain file)
A-6.4) AFFERMAZIONE VERA: /bin/ls esiste ed ha dimensione maggiore di 0
-------------------------------------------
TEST sui file /dev
-------------------------------------------
B-1.1) AFFERMAZIONE VERA: /dev/cdrom è una device a blocchi
B-2.2) AFFERMAZIONE VERA: /dev/tty è una device a caratteri
B-4.3) AFFERMAZIONE VERA: /dev/initctl e' una pipe FIFO
-------------------------------------------
TEST sui file /etc/httpd/conf/httpd.conf
Diritti httpd.conf:  -rw-r--r--
-------------------------------------------
C-1.1) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'
                          leggibile (per il proprietario)
C-2.2) AFFERMAZIONE VERA: /etc/httpd/conf/httpd.conf e'
                          scrivibile (per il proprietario)
-------------------------------------------
TEST su questo file batch: file.sh
-------------------------------------------
Diritti con 0000 di /home/studente/file.sh:  ----------
Diritti con 1000 di /home/studente/file.sh:  ---------T
D-4.1) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sticky bit
Diritti con 2000 di /home/studente/file.sh:  ------S---
D-5.2) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo sgid bit
Diritti con 4000 di /home/studente/file.sh:  ---S------
D-6.3) AFFERMAZIONE VERA: /home/studente/file.sh ha settato lo suid bit
-------------------------------------------
TEST sui files descriptor
-------------------------------------------
E-1) Questo messaggio non si vede se scrivo: file_oper.csh < input.txt
E-2) Questo messaggio non si vede se scrivo:
                      file_oper.csh >& stderr.txt
                      file_oper.csh > output.txt
E-3) Questo messaggio non si vede se scrivo:
                      file_oper.csh >& stderr.txt
-------------------------------------------
TEST vari
-------------------------------------------
F-1.1) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione uguale a 0
F-4.2) AFFERMAZIONE VERA: /home/studente/ciao esiste e ha dimensione maggiore di 0


Gli operatori di confronto numerici in sh e csh sono:


 sh csh Operatori di confronto numerici
-eq == UGUALE
-ne != DIVERSO
-lt < MINORE
-le <= MINORE E UGUALE
-gt > MAGGIORE
-ge >= MAGGIORE O UGUALE


Le diverse condizioni possono essere legate tra loro tramite gli operatori logici AND, OR, NOT

 

Operatori logici - bash, sh
-a AND
-o OR
! NOT
Operatori logici - csh
&& AND
|| OR
! NOT


ESEMPIO IF 4.A - AND:


per controllare che due o più condizioni siano soddisfatte dobbiamo utilizzare l'operatore logico AND.
L'esempio proposto controlla che un punto (x,y) stia nel primo quadrante del piano cartesiano (origine inclusa).


Shell sh - File AND.sh Shell csh - File AND.csh
read -p "Digita X:" X
read -p "Digita Y:" Y
# In alternativa a [ expr ] posso usare (( expr ))
#   if (( $X >= 0 && $Y >= 0 )); then
if [ $X -ge 0 -a $Y -ge 0 ]; then
  echo "($X,$Y) E' NEL PRIMO QUADRANTE"
else
  echo "($X,$Y) NON E' NEL PRIMO QUADRANTE"
fi
#!/bin/csh -f
echo -n "Digita X:";set X=$<
echo -n "Digita Y:";set Y=$<
if ( $X >= 0 && $Y >= 0 ) then
  echo "($X,$Y) E' NEL PRIMO QUADRANTE"
else
  echo "($X,$Y) NON E' NEL PRIMO QUADRANTE"
endif

ESEMPIO IF 4.B - OR:

per verificare che tra diverse condizioni almeno una condizione sia soddisfatta occorre utilizzare l'operatore logico OR.
L'esempio proposto controlla che un punto (x,y) sia esterno al primo quadrante del piano cartesiano.

Shell sh - File OR.sh Shell csh - File OR.csh
read -p "Digita X:" X
read -p "Digita Y:" Y
# In alternativa a [ expr ] posso usare (( expr ))
#    if (( $X < 0 || $Y < 0 )); then
if [ $X -lt 0 -o $Y -lt 0 ]; then
  echo "($X,$Y) NON E' NEL PRIMO QUADRANTE"
else
  echo "($X,$Y) E' NEL PRIMO QUADRANTE"
fi
#!/bin/csh -f
echo -n "Digita X:";set X=$<
echo -n "Digita Y:";set Y=$<
if ( $X < 0 || $Y < 0 ) then
  echo "($X,$Y) NON E' NEL PRIMO QUADRANTE"
else
  echo "($X,$Y) E' NEL PRIMO QUADRANTE"
endif

ESEMPIO IF 4.C - NOT:

La negazione talvolta può essere utile per semplificare la condizione di test

Shell sh - File NOT.sh Shell csh - File NOT.csh
#!/bin/sh
read -p "Digita X:" X
read -p "Digita Y:" Y
# In alternativa a [ expr ] posso usare (( expr ))
#  if !(( $X < 0 || $Y < 0 )); then
if ! [ $X -lt 0 -o $Y -lt 0 ]; then
  echo "($X,$Y) E' NEL PRIMO QUADRANTE"
else
  echo "($X,$Y) NON E' NEL PRIMO QUADRANTE"
fi
#!/bin/csh -f
echo -n "Digita X:";set X=$<
echo -n "Digita Y:";set Y=$<
if !( $X >= 0 && $Y >= 0 ) then
  echo "($X,$Y) NON E' NEL PRIMO QUADRANTE"
else
  echo "($X,$Y) E' NEL PRIMO QUADRANTE"
endif

GOTO - ISTRUZIONE DI SALTO


 

L' istruzione GOTO, presente solo in csh, consente di saltare con l'esecuzione ad un altro punto dello script. La sintassi generica è la seguente

 

GOTO Etichetta


ESEMPIO GOTO 1

In questo esempio viene stampata la sequenza dei primi 10 numeri interi.

 

Shell csh - file goto.csh
#!/bin/csh -f
@ i=1
inizio:
echo -n "$i "
@ i++
if ( $i <= 10 ) goto inizio
echo "Fine"


CASE - ISTRUZIONE DI SELEZIONE MULTIPLA


 

L' istruzione CASE consente di selezionare una sequenza di esecuzione controllando N condizioni. La sintassi generica è la seguente
 

Sintassi CASE in bash o sh Sintassi CASE in csh
case variabile in
   pattern1.0 [|pattern1.1] ... )
      comando1.1
      ...
      command1.K1
      ;;
   [pattern2.0 [|pattern2.1] ... )
      comando2.1
      ...
      command2.K2
      ;;
   ...
   patternJ.0 [|patternJ.1] ... )
      comandoJ.1
      ...
      commandJ.Kj
      ;;
   ...
   * )
      comandoN.1
      ...
      commandN.KN
      ;;]
esac
switch(variabile)
   pattern1.0:
   [pattern1.1]:
   ...:
      comando1.1
      ...
      command1.K1
      breaksw
   pattern2.0:
   [pattern2.1]:
   ...:
      comando2.1
      ...
      command2.K2
      breaksw
   ...
   patternJ.0:
   [patternJ.1]:
   ...:
      comandoJ.1
      ...
      commandJ.KJ
      breaksw
   ...
   default:
      comandoN.1
      ...
      commandN.KN
endsw

 

Questa costrutto può essere sostituito con questa sequenza IF ELSE:
 

Sintassi IF equivalente al CASE in bash o sh Sintassi IF equivalente al CASE in csh
if condizione1; then
   comando1.1
   ...
   command1.K1
[elif condizione2; then
   comando2.1
   ...
   command2.K2
...
[else
   comandoN.1
   ...
   commandN.KN
fi
if (condizione1) then
   comando1.1
   ...
   command1.K1
[else if (condizione2) then
   comando2.1
   ...
   command2.K2
...
[else
   comandoN.1
   ...
   commandN.KN
endif

ESEMPIO CASE 1

L'esempio seguente valuta il tipo di carattere premuto. Ecco due script: il primo in sh e il secondo, equivalente, in csh

Shell sh - File case.sh
read -n1 -r -p "digita un tasto: " Tasto
echo;
case "$Tasto" in
   # [[:upper:]] ) echo "Hai digitato: $Tasto :-> Lettera MAIUSCOLA";;
   [A-Z] ) echo "Hai digitato: $Tasto :-> Lettera MAIUSCOLA";;
   # [[:lower:]] ) echo "Hai digitato: $Tasto :-> Lettera Minuscola";;
   [a-z] ) echo "Hai digitato: $Tasto :-> Lettera minuscola";;
   [0-9] ) echo "Hai digitato: $Tasto :-> un numero";;
   "*" | "+" | "-" | "/" | "^" ) echo "Hai digitato: $Tasto :-> Operatore aritmetico";;
   # oppure: [\*+\-\/^] ) ... ;;
   [\{\}\[\]\(\)] ) echo "Hai digitato: $Tasto :-> Parentesi";;
   # oppure: "{"|"}"|"("|")"|"["|"]" ) ... ;;
   * ) echo "Altro";;
esac

Shell csh - file case.csh
#!/bin/csh -f
echo -n "digita un tasto: "
set Tasto = `head -1`
switch ("$Tasto")
   case [a-z]:
      echo "Hai digitato: $Tasto :-> Lettera minuscola"
      breaksw
   case [A-Z]:
      echo "Hai digitato: $Tasto :-> Lettera MAIUSCOLA"
      breaksw
   case [0-9]:
      echo "Hai digitato: $Tasto :-> Un numero"
      breaksw
   case ")":
   case "(":
   case "]":
   case "}":
   case "{":
   case [\[]:
      echo "Hai digitato: $Tasto :-> Parentesi"
      breaksw
   case [\*+/\^-]:
   # O in alternativa
   # case "+":
   # case "-":
   # case "/":
   # case [\*]:
   # case \^:
      echo "Hai digitato: $Tasto :-> Operatore aritmetico"
      breaksw
   default:
      echo "Altro"
endsw

si osservi che:
- nel case della csh i : non devono essere preceduti da spazi.
- Più condizioni abbinate devono essere scritte su linee distinte
- nello script sh la condizione [A-Z] è stata scritta per prima poichè, nella versione di RedHat 7.2 utilizzata per le prove, la sequenza [a-z] accetta sia le lettere maiuscole che minuscole
- si osservi la modalità alternativa per l'input proposta nell'esempio csh (ma è applicabile anche in sh) basata sull'istruzione set Tasto = `head -1` che consente la lettura di una frase fino all'invio. Nel caso si voglia leggere solo il primo carattere della stringa di input digitata si può utilizzare il comando: set Tasto = `head --bytes=1`.

L'esecuzione dei due script fornisce i seguenti output

Shell Sh - esecuzione di case.sh Shell Csh - esecuzione di case.csh
sh-2.05$ ./case.sh
digita un tasto: a
Hai digitato: a :-> Lettera minuscola
sh-2.05$ ./case.sh
digita un tasto: A
Hai digitato: A :-> Lettera MAIUSCOLA
sh-2.05$ ./case.sh
digita un tasto: 1
Hai digitato: 1 :-> un numero
sh-2.05$ ./case.sh
digita un tasto: (
Hai digitato: ( :-> Parentesi
sh-2.05$ ./case.sh
digita un tasto: )
Hai digitato: ) :-> Parentesi
sh-2.05$ ./case.sh
digita un tasto: {
Hai digitato: { :-> Parentesi
sh-2.05$ ./case.sh
digita un tasto: }
Hai digitato: } :-> Parentesi
sh-2.05$ ./case.sh
digita un tasto: [
Hai digitato: [ :-> Parentesi
sh-2.05$ ./case.sh
digita un tasto: ]
Hai digitato: ] :-> Parentesi
sh-2.05$ ./case.sh
digita un tasto: +
Hai digitato: + :-> Operatore aritmetico
sh-2.05$ ./case.sh
digita un tasto: *
Hai digitato: * :-> Operatore aritmetico
sh-2.05$ ./case.sh
digita un tasto: -
Hai digitato: - :-> Operatore aritmetico
sh-2.05$ ./case.sh
digita un tasto: /
Hai digitato: / :-> Operatore aritmetico
sh-2.05$ ./case.sh
digita un tasto: ^
Hai digitato: ^ :-> Operatore aritmetico
sh-2.05$ ./case.sh
digita un tasto: !
Altro
sh-2.05$ ./case.sh
digita un tasto: $
Altro
[studente@localhost ~]$ ./case.csh
digita un tasto: a
Hai digitato: a :-> Lettera minuscola
[studente@localhost ~]$ ./case.csh
digita un tasto: A
Hai digitato: A :-> Lettera MAIUSCOLA
[studente@localhost ~]$ ./case.csh
digita un tasto: 1
Hai digitato: 1 :-> Un numero
[studente@localhost ~]$ ./case.csh
digita un tasto: {
Hai digitato: { :-> Parentesi
[studente@localhost ~]$ ./case.csh
digita un tasto: }
Hai digitato: } :-> Parentesi
[studente@localhost ~]$ ./case.csh
digita un tasto: )
Hai digitato: ) :-> Parentesi
[studente@localhost ~]$ ./case.csh
digita un tasto: (
Hai digitato: ( :-> Parentesi
[studente@localhost ~]$ ./case.csh
digita un tasto: ]
Hai digitato: ] :-> Parentesi
[studente@localhost ~]$ ./case.csh
digita un tasto: [
Hai digitato: [ :-> Parentesi
[studente@localhost ~]$ ./case.csh
digita un tasto: +
Hai digitato: + :-> Operatore aritmetico
[studente@localhost ~]$ ./case.csh
digita un tasto: -
Hai digitato: - :-> Operatore aritmetico
[studente@localhost ~]$ ./case.csh
digita un tasto: *
Hai digitato: * :-> Operatore aritmetico
[studente@localhost ~]$ ./case.csh
digita un tasto: /
Hai digitato: / :-> Operatore aritmetico
[studente@localhost ~]$ ./case.csh
digita un tasto: ^
Hai digitato: ^ :-> Operatore aritmetico
[studente@localhost ~]$ ./case.csh
digita un tasto: $
Altro
[studente@localhost ~]$ ./case.csh
digita un tasto: !
Altro


ecco gli script equivalenti scritti con il construtto if

 

Shell sh - File ifcase.sh Shell sh - File ifcase.csh
#!/bin/sh
read -n1 -r -s -p "premi un tasto: " Tasto
echo ""
if [ "\\$Tasto" = "\*" ]; then
   echo "Hai digitato: $Tasto :-> Operatore aritmetico"
elif [ -z `echo $Tasto|tr -d 'a-z'` ]; then
   echo "Hai digitato: $Tasto :-> Lettera minuscola"
elif [ -z `echo $Tasto|tr -d 'A-Z'` ]; then
   echo "Hai digitato: $Tasto :-> Lettera MAIUSCOLA"
elif [ -z `echo $Tasto|tr -d '0-9'` ]; then
   echo "Hai digitato: $Tasto :-> Un numero"
elif [ -z `echo $Tasto|tr -d '\{\}\[\]\(\)'` ]; then
   echo "Hai digitato: $Tasto :-> Parentesi"
elif [ -z `echo $Tasto|tr -d '+\-\/^'` ]; then
   echo "Hai digitato: $Tasto :-> Operatore aritmetico"
else
   echo "Altro"
fi
#!/bin/csh -f
echo -n "digita un tasto: "
set Tasto = `head --bytes=1`
if ( `echo "$Tasto"|tr -d "{}[]()"` == "" ) then
   echo "Hai digitato: $Tasto :-> Parentesi"
else if ( `echo "$Tasto"|tr -d '\*+\-\/^'` == "" ) then
   echo "Hai digitato: $Tasto :-> Operatore aritmetico"
else if ( "$Tasto" =~ [a-z]) then
   echo "Hai digitato: $Tasto :-> Lettera minuscola"
else if ( "$Tasto" =~ [A-Z]) then
   echo "Hai digitato: $Tasto :-> Lettera MAIUSCOLA"
else if ( "$Tasto" =~ [0-9]) then
   echo "Hai digitato: $Tasto :-> Un numero"
else
   echo "Altro"
endif

Si osservi che il comando tr (translate) con l'opzione -d elimina da una stringa le lettere indicate, pertanto l'istruzione echo $Tasto | tr -d 'caratteri' elimina dalla variabile $Tasto tutti i caratteri indicati nella stringa 'caratteri'

FOR - ISTRUZIONE ITERATIVA


La sintassi del comando for è la seguente

Sintassi FOR in bash o sh Sintassi FOREACH in csh
for variabile [in Insieme]
do
   IstruzioneFOR
   [break]
   [continue]
done
foreach variabile (Insieme)
   IstruzioneFOR
   [break]
   [continue]
end


L'istruzione break interrompe il ciclo for e l'esecuzione prosegue con l'istruzione successiva a done (sh)/ end (csh). L'istruzione continue interrompe l'iterazione corrente e l'esecuzione riprende con l'iterazione successiva. L
'insieme è un elenco di valori come si vede in questo esempio con sh:

ESEMPIO FOR 1

 

Shell sh - File pianeti.sh
#!/bin/sh
for Pianeti in Mercurio Venere Terra Marte "Fascia degli Asteroidi" Giove Saturno Urano Nettuno Plutone 
do
   echo $Pianeti 
done

 

Ecco l'esempio equivalente scritto con la shell csh

 

Shell csh - File pianeti.csh
#!/bin/csh -f
foreach Pianeti (Mercurio Venere Terra Marte "Fascia degli Asteroidi" Giove Saturno Urano Nettuno Plutone)
   echo $Pianeti
end


Si osservi l'uso dei doppi apici " nel caso un valore presenti al suo interno degli spazi.

ESEMPIO FOR 2


Se la parte [in Insieme] in sh viene omessa allora il FOR utilizza come elenco di valori quelli scritti sulla linea dil comando dopo il nome del batch. In csh il comando equivalente richiede invece l'uso della variabile speciale $*
 

Shell sh - File args.sh Shell csh - file args.csh
#!/bin/sh
for c; do echo $c; done 
#!/bin/csh -f
foreach c ( $* )
   echo $c
end

 

ESEMPIO FOR 3

L'
insieme analizzato dal for può essere generato con l'output di un comando. Nell'esempio successivo vengono rielaborati (in sh) tutti i files che iniziano per L della cartella di sistema /bin.

 

Shell sh - File File_L.sh
for c in `ls /bin/l*`; do echo $c; done


Ecco due esempi in csh che svolgono la stessa funzione di File_L.sh.
Nelle parentesi del foreach di csh è possibile scrivere o il comando (delimitato dal backquote) oppure il solo argomento utilizzato nel comando
ls. Il batch file_L.sh con csh diventano:
 

Shell csh - File File_L.csh
#!/bin/csh -f
foreach c (/bin/l*)
   echo $c
end
#!/bin/csh -f
foreach c (`ls /bin/l*`)
   echo $c
end


Potendo inserire come generatore di insieme un qualsiasi comando UNIX potremo anche analizzare ricorsivamente il contenuto di una directory (esempio con ls -R).

ESEMPIO FOR 4


Nel prossimo esempio vengono conteggiati:
- tutti i files con estensione .txt presenti nel nostro sistema (il $ in fondo all'argomento del comando grep indica che l'estensione va cercata in fondo al nome!)
- i files che non risultano accessibili dall'utente corrente.

Relativamente alla seconda statistica si osservi che la risposta del comando
ls relativa ad un file non accessibile ha una struttura simile a questo esempio:

ls: /var/log/samba: Permission denied

Tale messaggio di errore viene inviato allo standard error. Per evitare che la variabile
$i, utilizzata dal for/foreach, assuma, per ogni riga di output, 4 valori distinti ed esattamente: ls:, /var/log/samba:, Permission, denied è necessario ridirigere l'output, tramite una pipe, verso il comando cut -c 1. In questo modo si estrae, relativamente ad ogni file non accessibile, solo il primo carattere e quindi una sola valorizzazione di $i per ogni riga.
 

Shell sh - File ContaFile.sh
let N=0
for i in `ls -R / 2>/dev/null | grep .txt$`; do let N=N+1; done
echo $N Files di testo
let N=0
for i in `((ls -R / 3>&2 2>&1 >&3) 2>/dev/null)|cut -c 1`
do
   let N=N+1
done
echo $N Files non accessibili

 

L'equivalente in csh è il seguente [si ricordi che la ridirezione >& ridirige sia lo stdout che lo stderr - quindi per simulare la separazione tra stderr e stdout in csh occorre scrivere qualcosa del tipo: (comando > fileOut) >& fileErr ]:
 

Shell sh - File ContaFile.csh
#!/bin/csh -f
@ N=0
foreach i (`(((ls -R / > pippo) >& /dev/null); grep .txt$ pippo)`)
  @ N++
end
echo $N Files di testo
@ N=0
foreach i (`(ls -R / >/dev/null) |& cut -c 1`)
   @ N++
end
echo $N Files non accessibili

 

Ecco un esempio di esecuzione

 

Shell Sh - esecuzione di ContaFile.sh Shell csh - esecuzione di ContaFile.csh
sh-2.05$ whoami
studente
sh-2.05$ ./ContaFile.sh
542 Files di testo
76 Files non accessibili
sh-2.05$ su -s /bin/sh root
Password:
sh-2.05# ./ContaFile.sh
542 Files di testo
0 Files non accessibili
sh-2.05#
[studente@localhost ~]$ whoami
studente
[studente@localhost ~]$ ./ContaFile.csh
542 Files di testo
76 Files non accessibili
[studente@localhost ~]$ su -s /bin/csh root
Password:
[studente@localhost studente]# ./ContaFile.csh
542 Files di testo
0 Files non accessibili
[studente@localhost studente]#


ESEMPIO FOR 5

In quest'altro esempio vengono conteggiate tutte le cartelle e i files presenti nel nostro sistema [si ricordi che:
find <path> -type d => elenca le directory, find <path> -type f => elenca i files,  find <path> -type l => elenca i link simbolici]
 

Shell sh - File ContaDIrFile.sh
let N=0
let F=0
for i in `find / -type d 2>/dev/null|cut -c 1`; do let N=N+1; done
for i in `find / -type f 2>/dev/null|cut -c 1`; do let F=F+1; done
echo $N Cartelle - $F Files

ed ecco la soluzione scritta in csh

Shell sh - File ContaDIrFile.csh
#!/bin/csh -f
@ N=0
@ F=0
foreach i (`(find / -type d) |& grep -v "Permission denied" | cut -c 1`)
 @ N++
end
foreach i (`(find / -type f) |& grep -v "Permission denied" | cut -c 1`)
 @ F++
end
echo $N Cartelle - $F Files

 

In sh esiste anche un'altra sintassi per il for molto simile a quella usata i c++:

 

Sintassi alternativa del FOR in bash o sh
for ((contatore=ValoreIniziale, Contatore Relazione ValoreFinale ; Passo)
do
   IstruzioneFOR
   [break]
   [continue]
done
 


ESEMPIO FOR 6


Ecco due script equivalenti che stampano la sequenza dei numeri dispari da 1 a 10 [1 3 5 7 9]

 

Shell sh - File Iteraz.sh Shell sh - File Iteraz2.sh
let N=10
for ((a=1; a <= N ; a++))
do
   if [ $(($a%2)) -eq 0 ]; then
      continue
   fi
   echo -n "$a "
done
echo;
for ((a=1; ; a++))
do
   if [ $(($a%2)) -eq 0 ]; then continue ; fi
   if [ $a -gt 10 ]; then break ; fi
   echo -n "$a "
done
echo;


Si osservi che:
- L'istruzione continue termina il ciclo corrente e l'esecuzione prosegue con l'iterazione successiva partendo da a++
- L'istruzione
break termina il ciclo for corrente e prosegue con l'istruzione successiva a done

WHILE - Istruzione Iterativa


La sintassi del comando While è la seguente:

Sintassi WHILE in bash o sh Sintassi WHILE in csh
while [CondizioneVera]
do
   IstruzioneWHILE
   [break]
   [continue]
done
while (CondizioneVera)
   IstruzioneWHILE
   [break]
   [continue]
end


ESEMPIO WHILE 1

Il primo esempio stampa tutti gli argomenti passati ad un programma batch (si ricordi che
$# indica il numero di argomenti passati al batch file)
 

Shell sh - File elenca.sh Shell csh - file elenca.csh
#!/bin/sh
while [ $# -gt 0 ]
do
  echo $1
  shift
done
#!/bin/csh -f
while ($#argv != 0 )
   echo $argv[1]
   shift
end


ESEMPIO WHILE 2

il seguente script stampa una sequenza di numeri casuali compresi tra 1 e 80
 

Shell sh - File while_RND.sh
Massimo=80
N=0
Numero=$(($RANDOM%100))
while [ $Numero -lt $Massimo ]
do
  let N++
  echo -n "$N^) $Numero "
  Numero=$(($RANDOM%100))
done
echo;echo "Ho generato $N numeri inferiori a $Massimo - Ultimo: $Numero"

ed ecco il suo equivalente in csh. Si noti che in csh non esiste la variabile d'ambiente $RANDOM per cui per generare un numero casuale sfrutto l'ambiente offerto dal comando awk.

Shell sh - File WHILE_digita.csh
#!/bin/csh -f
@ Massimo=80
@ N=0
@ Numero=`awk 'BEGIN {srand();x=sprintf("%d",rand()*100); print x}'`
while ($Numero < $Massimo)
  @ N++
  echo -n "$N^) $Numero "
  @ Numero=`awk 'BEGIN {srand();x=sprintf("%d",rand()*100); print x}'`
end
echo;echo "Ho generato $N numeri inferiori a $Massimo - Ultimo: $Numero"

ESEMPIO WHILE 3

il seguente script richiede di digitare qualcosa finchè non si digita fine

Shell sh - File WHILE_digita.sh
Frase=""
while [ "$Frase" != "Fine" ]
do 
   read -p "Digita qualcosa (Fine per uscire) >> " Frase 
   if [ "$Frase" != "Fine" ]; then
      echo "Hai digitato: $Frase"
   else
      echo "Programma terminato!"
   fi
done

ed ecco il suo equivalente in csh

Shell sh - File WHILE_digita.csh
#!/bin/csh -f
set Frase=""
while ( "$Frase" != "Fine" )
   echo -n "Digita qualcosa (Fine per uscire) >> : "
   set Frase=$<
   if ( "$Frase" != "Fine" ) then
      echo "Hai digitato: $Frase"
   else
      echo "Programma terminato!"
   endif
end

in sh esiste un'altro construtto iterativo:  until. Contrariamente al  while il loop termina quando la condizione diventa vera:

Sintassi UNTIL in csh
Until [CondizioneFalsa]
do
   IstruzioneUNTIL
done

 

ESEMPIO UNTIL 1

il programma precedente con until diventa:
 

Shell sh - File UNTIL_Digita.sh
Frase=""
until [ "$Frase" == "Fine" ]
do 
   read -p "Digita qualcosa (Fine per uscire) >> " Frase 
   if [ "$Frase" != "Fine" ]; then
      echo "Hai digitato: $Frase"
   else
      echo "Programma terminato!"
   fi
done

 

FUNCTION


Nella shell sh è possibile definire delle funzioni. La sintassi per le funzioni è la seguente (si noti l'assenza della dichiarazione degli argomenti):

Sintassi Function in sh Sintassi alias
function NomeFunzione {
    IstruzioneFUNZIONE
}

oppure

NomeFunzione() {
    IstruzioneFUNZIONE
}
alias NomeAlias 'Comando'

 

Nella csh non esistono le funzioni ma è possibile costruire qualcosa di simile con il comando alias
 

Shell sh - File funzione.sh Shell csh
# La dichiarazione della funzione deve essere 
# fatta prima della sua chiamata
Scrivi ()
{
   echo "Scrivo qualcosa"
   echo "Ora esco da \"Scrivi()\"."
}
# Esempio di chiamata funzione.
# Scrivi
[root@localhost ~]# alias Scrivi 'echo "Scrivo qualcosa";echo "Ora esco da "\""Scrivi"\""."'
[root@localhost ~]# Scrivi
Scrivo qualcosa
Ora esco da "Scrivi".
[root@localhost ~]# alias calcola '@ z = (\!*) ; echo $z'
[root@localhost ~]# calcola 1 + 2 * 7
15
[root@localhost ~]# 

Le funzioni sono come dei mini-scripts: possono accettare dei parametri, usano delle variabili locali e restituiscono dei valori agli script chiamanti.
Le funzioni interpretano i parametri in modo posizionale. Il nome dei parametri posizionali è lo stesso visto per quelli usati negli script batch ovvero $#, $*, $0, $1... .
Nel comando alias (csh) le seguenti stringhe indicano:
- \!* : l'elenco dei parametri passati (corrisponde alla variabile sh $*),
- \!$ : l'ultimo parametro (corrisponde alla variabile sh ${!#}),
- \!:1 o \!^ : il primo parametro (corrisponde a $1),
- \!! : l'intero comando,
- \!:<n>* : elenco dei parametri a partire dall'n-esimo (compreso),
- \!:<n>-<m> : elenco dei parametri a partire dall'n-esimo (compreso) fino all'm-esimo(compreso),
- \!:<n> : l'n-esimo argomento.

Ecco un esempio di utilizzo dei parametri con le funzioni (sh) e con l'alias (csh)

Shell sh - File funzione.sh Shell csh
TestParametri ()
{
   let i=1
   echo "Nr. argomenti: $#"
   echo "Elenco argomenti: $*"
   for a
   do
       echo "$i Parametro: $a"
       let i=i+1
   done
}
TestParametri a b c d
[root@localhost ~]# alias TestParametri 'set s="\!*" ; set y=(\!*) ; \
                    echo Nr. argomenti: ${#y} ; \
	            echo "Elenco argomenti: $s" ; \
                    @ i = 1 ; \
	            foreach a ( $s ) \
       			echo "$i Parametro: $a" ; \
   		        @ i++ ; \
	            end'
[root@localhost ~]# TestParametri a b c
Nr. argomenti: 3
Elenco argomenti: a b c
1 Parametro: a
2 Parametro: b
3 Parametro: c

TRUCCHETTI/ESEMPI


 

SCRITTURA SU FILE:

Alla base di questo esempio abbiamo i simboli di ridirezione > e >>. L'esempio, funzionante sia in csh che sh, crea un file Testo.txt di 4 righe
 

echo Prima riga   > TESTO.TXT
echo Seconda riga >>TESTO.TXT
echo Terza riga   >>TESTO.TXT
echo -- Creato il `date +%d/%m/%Y` alle `date +%H.%M.%S`  >>TESTO.TXT
echo Visualizza il FILE
echo ------------------------
cat TESTO.TXT
echo ------------------------

ATTESA:

Per sospendere l'esecuzione di uno script per un certo numero di secondi possiamo usare in sh l'istruzione read

Shell sh - File wait.sh
read -t3 -dX -s -p "Attendere 3 secondi (premi X per terminare) ..." ; echo

In csh, per sospendere l'esecuzione di uno script per un certo numero di secondi, possiamo utilizzare la shell sh come se fosse un comando a cui passiamo, sotto forma di stringa (delimitata da '), la stessa istruzione di wait usata nell'esempio precedente.

Shell csh - File wait.csh
#!/bin/csh -f
sh -c 'read -t3 -dX -s -p "Attendere 3 secondi (premi X per terminare) ... " ; echo'

Si osservi che l'opzione -c di sh richiama la shell sh solo per eseguire il comando passato come argomento e poi restituisce di nuovo il controllo alla csh chiamante.

GENERAZIONE NUMERI CASUALI:

In sh la variabile $RANDOM può essere utilizzata per generare dei numeri casuali compresi da 0 a 32.767 (intero a 16 bit). L'esempio proposto genera un numero intero compreso nell'intervallo[1,MAX_N]

Shell sh - File RANDOM.sh
let MAX_N=10
let N=$RANDOM
let N=$(($N%$MAX_N))+1
echo N=$N

In csh non esiste la variabile $RANDOM per cui utilizzo l'utility awk che fornisce una sorta di ambiente di programmazione (simile al C) orientato alla manipolazione delle stringhe

Shell sh - File RANDOM.csh
#!/bin/csh -f
@ MAX_N=10
@ N=`awk 'BEGIN {srand();x=sprintf("%d",rand()*100); print x}'`
@ N = $N % $MAX_N + 1
echo N=$N

FUNZIONE PER DETERMINARE IL GIORNO DI PASQUA PARTENDO DALL'ANNO 

Vediamo ora un esempio di comando batch sh che utilizza una funzione per determinare il giorno di pasqua partendo dall'anno:

Shell sh - File GiorniPasquali.sh
Pasqua ()
{
   local a
   let year=$1
   Mesi=(Gennaio Febbraio Marzo Aprile Maggio Giugno
         Luglio Agosto Settembre Ottobre Novembre Dicembre)
   if [ $year -ge 1583 ]; then
      # Calendario gregoriano
      a=$((year % 19))
      b=$((year / 100))
      c=$((year % 100))
      d=$((b / 4))
      e=$((b % 4))
      f=$(( (b + 8)/ 25))
      g=$(( (b - f + 1) / 3))
      h=$(( (19 * a + b - d - g + 15) % 30))
      a=$((year % 19))
      b=$((year / 100))
      c=$((year % 100))
      i=$(( c / 4))
      k=$(( c % 4))
      l=$(( (32 + 2 * e + 2 * i - h - k) % 7))
      m=$(( (a + 11 * h + 22 * l) / 451))
      n=$(( h + l - 7 * m + 114))
      mese=${Mesi[$((n / 31 -1))]}
      giorno=$(( n % 31 + 1))
   else
      # Calendario Giuliano
      a=$((year % 4))
      b=$((year % 7))
      c=$((year % 19))
      d=$(( (19 * c + 15) % 30))
      e=$(((2 * a + 4 * b - d + 34) % 7))
      f=$((d + e + 114))
      mese=${Mesi[$((f / 31 - 1))]}
      giorno=$((f % 31 + 1))
   fi
   echo "$giorno $mese $year"
}
# uso:
# ./GiorniPasquali.sh 2000 2010
for ((a=$1; a <= $2 ; a++))
do
   Pasqua $a
done
echo;


Si osservi:
- le espressioni numeriche non vogliono spazi di separazione rispetto all'operatore di assegnamento
- la funzione non ha argomenti se non quelli deducibili posizionalmente
- l'istruzione return vuole un numero
- per evitare confusione sulla variabile a all'interno della funzione abbiamo la dichiarazione local
- nell'inizializzazione dell'array sono andato a capo senza inserire caratteri particolari
- per approfondire l'algoritmo che consente la determinazione del giorno di pasqua clicca su questo link

GESTIONE DI UN MENU

Ecco ora un esempio che consente la gestione di un semplice menu:

CONTA=""
while :
do
   clear
   echo "---------------------------------------"
   echo "ESEMPIO MENU $CONTA"
   echo "---------------------------------------"
   echo "1 - Menu A : Visualizza Ora"
   echo "2 - Menu B : Visualizza Data"
   echo "3 - Menu C : Azzera conteggio"
   echo "4 - Exit"
   echo "---------------------------------------"
   echo;
   opt=""
   read -t2 -n1 -s -p "digita un'opzione: [1-4]: " opt
   if [ -n $opt ]; then
      echo;
   fi
   case "$opt" in
      ""  ) CONTA="$CONTA*";;
      "1" ) echo "Ora corrente `date +%H.%M.%S`";
            echo -n "Premi un tasto per continuare ...";
            read -n1 enterKey;;
      "2" ) echo "Data corrente: `date +%d/%m/%Y`";
            echo -n "Premi un tasto per continuare ...";
            read -n1 enterKey;;
      "3" ) CONTA="";;
      "4" ) echo "Bye Bye $USER";
            break;;
      * ) echo "$opt: opzione errata! Digita un numero tra 1 e 4";
          echo -n "Premi un tasto per continuare ...";
          read -n1 enterKey;;
   esac
   if [ "$CONTA" == "******" ]; then
      CONTA=""
   fi
done

l'output ottenuto è il seguente:

---------------------------------------
ESEMPIO MENU ***
---------------------------------------
1 - Menu A : Visualizza Ora
2 - Menu B : Visualizza Data
3 - Menu C : Azzera conteggio
4 - Exit
---------------------------------------

digita un'opzione: [1-4]:

si noti come il passare del tempo venga evidenziato con l'aggiunta di * a fianco del titolo "ESEMPIO MENU".

L'UTILITY AWK E LE ESPRESSIONI REGOLARI


 

L'utility awk (acronimo di Aho Weinberger Kerningan) richiede:
- un programma (nel linguaggio di awk),
- un'espressione regolare (pattern) 
- un testo da esaminare (che può arrivare anche tramite una pipe).
Il testo analizzato da awk viene visto come una sequenza di linee contenenti dei valori separati da opportuni separatori. Il sorgente del programma in awk può essere digitato direttamente sulla linea di comando oppure scritto in un file esterno. Ogni istruzione di awk ha una sintassi di questo tipo:

pattern {comandi in linguaggio awk}

 

Vediamo ora alcuni esempi:

a) Il primo esempio

awk '{print $3}' srcfile

 

stampa a video la terza colonna del file srcfile. La separazione in colonne/campi dei valori all'interno di una linea è determinato dalla presenza di  una qualsiasi combinazione di spazi o tab.  


b) L’opzione -F permette di specificare un separatore per le colonne/campi diverso da quelli predefiniti (spazio e tabulazione). In questo esempio il comando awk (privo del  pattern) utilizza come separatore i due punti :

awk -F: '{print $5,$1}' /etc/passwd

le variabili
$5 e $1 si riferiscono rispettivamente al quinto e al primo campo/colonna di ogni linea del file /etc/passwd; il carattere , inserisce uno spazio fra le due colonne/campi visualizzati; 

c) Ogni linea presente nel file analizzato  viene verificata con i pattern proposti e se c’è corrispondenza viene intrapresa l’azione racchiusa tra {} altrimenti niente. Quindi con il comando:


awk '/sechi/{print $0}' srcfile
 

verranno visualizzate tutte le righe contenenti la parola "sechi" (attenzione è case sensitive!). La variabile
$0 si riferisce all’intera linea.

d) Nell'esempio successivo non è indicata alcuna azione da eseguire (quella che è solitamente racchiusa tra {}). In questo caso l'azione di default intrapresa  è print $0 ovvero la stampa, sullo standard output, delle linee che hanno soddisfatto i vincoli imposti dal pattern. Il comando seguente:

awk '/^da_qui/,/^a_qui/' srcfile

stampa tutte le linee di srcfile che risultano delimitate dalla linea che inizia con la sequenza "da_qui" fino a quella che inizia per "a_qui".

 

e) Un pattern può essere una espressione regolare (molto usate con il comando grep) o una condizione. Il comando awk converte automaticamente, quando il contesto lo richiede, un campo in numero. Nell'esempio successivo:

awk ‘$1 > 18{print $2}’ srcfile

verrà visualizzata la 2° colonna delle righe che hanno il primo campo maggiore di 18. Le espressioni regolari usano alcuni dei metacaratteri che vengono utilizzati anche dalla shell. Per questo motivo è necessario racchiuderle fra singolo apice.

 

f) Vediamo ora una serie di esempi. Tutti gli esempi analizzano il seguente file di testo (si noti la presenza di una linea vuota [contenente solo un invio]):

 

File ELENCO
m.1) Sechi Paolo
f.2) Rosi Bruna
m.3) Bianchi Pierpaolo
m.4) Sechi Giulio^

m.5) Paolotto Mario*
f.6) Bronchi Rosa-
f.7) Fiini Marcella*
m.8) Fabiana GianPaolo


Analizziamo ora alcune regole nella costruzione delle espressioni regolari
- l'uso delle [] indica un elenco di alternative (ad esempio con [az] verifico che contenga la lettera a oppure z)
- se all'interno delle [] la sequenza di lettere inizia per ^ allora viene verificata la negazione (ad esempio con [^az] verifico che non contenga la lettera a o z)
- se all'interno delle [] il carattere ^ non è all'inizio allora l'accento circonflesso ^ viene interpretato come semplice carattere. (ad esempio con [az^] verifico che contenga la lettera a oppure z oppure ^)
- il carattere ^ all'inizio dell'espressione regolare indica che il controllo va fatto all'inizio della linea (ad esempio ^[az] controlla che la linea inizi per a o z)
- il . (punto) indica un qualsiasi carattere ad esclusione dell'invio (newline). Se l'epressione regolare è composta dal solo . allora escludo dall'analisi le righe vuote
- il carattere $ in fondo all'espressione regolare indica che il controllo va fatto alla fine della linea (ad esempio [az]$ verifica che la linea finisca con a oppure z)
- il carattere * indica che il carattere che lo precede può apparire zero o più volte
- il carattere + indica che il carattere che lo precede può apparire una o più volte
- il carattere ? indica che il carattere che lo precede può apparire zero o una volta
- il carattere \ serve ad indicare che il carattere successivo  non va interpretato come un metacarattere
- la sequenza \< verifica la corrispondenza all’inizio di una parola
- la sequenza \> verifica la corrispondenza alla fine di una parola
- \<espressione_regolare\>" verifica la corrispondenza esatta su almeno una delle parole contenute nella linea (la stringa "espressione_irregolare" non verrà quindi riconosciuta)
- ^regolare$" verifica la corrispondenza esatta sul contenuto dell'intera linea
- un'espressione regolare, composta da "sottoespressioni regolari" collegate tra loro dalla | (pipe), risulta verificata se almeno una delle sottoespressioni risulta verificata (ad esempio: [iu|ie] estrae le righe che contengono la sottosequenza ie oppure iu)
- Sequenze combinate di caratteri racchiusi tra parentesi quadre verificano le possibili modalità di scrittura di una parola. Ad esempio "[Ss][Ii]" controlla che si sia scritta una delle seguenti sequenze: si, SI, Si, sI. L'espressione regolare "[0-9][0-9][0-9][0-9][0-9]" verifica la "compatibilità" di una stringa  con le modalità di scrittura di un codice di avviamento postale
.

 

Ed ora ecco gli esempi:


Pattern nessuno: - stampo senza condizioni tutto il file
- print senza argomenti equivale a print $0
sh-2.05$ awk '{print}' elenco
m.1) Sechi Paolo
f.2) Rosi Bruna
m.3) Bianchi Pierpaolo
m.4) Sechi Giulio^

m.5) Paolotto Mario*
f.6) Bronchi Rosa-
f.7) Fiini Marcella*
m.8) Fabiana GianPaolo

Pattern /Paolo/: stampo le righe che contengono Paolo
Pattern /paolo/: il risultato mostra che la selezione è
case sensitive
sh-2.05$ awk '/Paolo/{print}' elenco
m.1) Sechi Paolo
m.5) Paolotto Mario*
m.8) Fabiana GianPaolo
sh-2.05$ awk '/paolo/{print}' elenco
m.3) Bianchi Pierpaolo

Pattern /a$/: estraggo le linee che contengono una
a in fondo alla linea
Pattern /[a-]$/: estraggo le linee che contengono
una a oppure un meno in fondo alla linea
Pattern /^f.2) Rosi Bruna$/: estraggo le linee che
contengono esattamente la frase scritta tra ^ e $
Pattern /^$/: - stampo tutte le righe vuote del file
sh-2.05$ awk '/a$/{print}' elenco
f.2) Rosi Bruna
sh-2.05$ awk '/[a-]$/{print}' elenco
f.2) Rosi Bruna
f.6) Bronchi Rosa-
sh-2.05$ awk '/^f.2) Rosi Bruna$/' elenco
f.2) Rosi Bruna
sh-2.05$ awk '/^$/{print}' elenco
riga vuota
sh-2.05$ awk '/iu|ie/{print}' elenco
m.3) Bianchi Pierpaolo
m.4) Sechi Giulio^
Pattern /./: - stampo tutte le righe non vuote del file
Pattern /lo./: - stampo le righe contenenti la sequenza
"lo" non posizionato a fine riga (ovvero non seguito da
un invio)
Pattern /iu*l*i/: - stampo le righe contenenti sequenze
delimitate tra due i con all'interno sequenze di zero o più
u e zero o più l
Pattern /iu+l+i/: - stampo le righe contenenti sequenze
delimitate da due i con all'interno sequenze di uno o più
u e uno o più l
Pattern /ab?r?i/: - stampo le righe contenenti sequenze
delimitate da una a e una i con all'interno sequenze di
zero o una lettera b seguita da zero o una lettera
r
sh-2.05$ awk '/./{print $0}' elenco
m.1) Sechi Paolo
f.2) Rosi Bruna
m.3) Bianchi Pierpaolo
m.4) Sechi Giulio^
m.5) Paolotto Mario*
f.6) Bronchi Rosa-
f.7) Fiini Marcella*
m.8) Fabiana GianPaolo
sh-2.05$ awk '/lo./{print}' elenco
m.5) Paolotto Mario*
sh-2.05$ awk '/iu*l*i/{print}' elenco
m.4) Sechi Giulio^
f.7) Fiini Marcella*
sh-2.05$ awk '/iu+l+i/{print}' elenco
m.4) Sechi Giulio^
sh-2.05$ awk '/ab?r?i/{print}' elenco
m.5) Paolotto Mario*
m.8) Fabiana GianPaolo

Pattern /[uSG^-]/: estraggo le linee che contengono
almento uno di questi caratteri
Pattern /[$]$/: estraggo le linee che terminano con $
sh-2.05$ awk '/[uSG^-]/{print}' elenco
m.1) Sechi Paolo
f.2) Rosi Bruna
m.4) Sechi Giulio^
f.6) Bronchi Rosa-
m.8) Fabiana GianPaolo
sh-2.05$ awk '/[$]$/{print}' elenco
f.2) Rosi Bruna$
Pattern nessuno - imposto come separatore la parentesi ) :
stampo tutte le seconde colonne del file elenco
sh-2.05$ awk -F\) '{print $2}' elenco
 Sechi Paolo
 Rosi Bruna
 Bianchi Pierpaolo
 Sechi Giulio^

 Paolotto Mario*
 Bronchi Rosa-
 Fiini Marcella*
 Fabiana GianPaolo

Pattern /\<Paolo/: estraggo le linee che contengono
almeno una parola che inizia con Paolo
Pattern /Paolo\>/: estraggo le linee che contengono
almeno una parola che finisce con Paolo
Pattern /\<Paolo\>/: estraggo le linee che contengono
la parola intera Paolo
sh-2.05$ awk '/\<Paolo/{print}' elenco
m.1) Sechi Paolo
m.5) Paolotto Mario*
sh-2.05$ awk '/Paolo\>/{print}' elenco
m.1) Sechi Paolo
m.8) Fabiana GianPaolo
sh-2.05$ awk '/\<Paolo\>/' elenco
m.1) Sechi Paolo

Pattern /^[fm]/: stampo le linee che hanno all'inizio
della riga la lettera f o m (tutte quindi)
Pattern  /^f.2/: solo le linee che iniziano con f.2
sh-2.05$ awk '/^[fm]/{print}' elenco
m.1) Sechi Paolo
f.2) Rosi Bruna
m.3) Bianchi Raffaello
m.4) Sechi Giulio^
m.5) Paolotto Mario*
f.6) Bronchi Rosa-
f.7) Fiini Marcella*
m.8) Fabiana GianPaolo
sh-2.05$ awk '/^f.2/' elenco
f.2) Rosi Bruna

g) Concludiamo con ulteriori esempi:

Esempio g.1) Estraggo le linee che hanno il cognome che inizia per B e il nome che termina per
o oppure meno
sh-2.05$  awk '( $2~/^B/ && $3~/[o-]$/ ) { print }' elenco
m.3) Bianchi Pierpaolo
f.6) Bronchi Rosa-
sh-2.05$ awk '( $2~/^B/ && ($3~/o$/ || $3~/-$/ ) ) { print }' elenco
m.3) Bianchi Pierpaolo
f.6) Bronchi Rosa-

 

Si osservi che la ~ (tilde) consente di verificare se se un campo "rispetta" l'espressione regolare scritta dopo la tilde. La sequenza !~  equivale a "non rispetta"

 

Esempio g.2) Estraggo le linee che hanno il cognome che non inizia per B e un nome che termina per
o oppure meno
sh-2.05$  awk '( $2!~/^B/ && $3~/[o-]$/ ) { print }' elenco
m.1) Sechi Paolo
m.8) Fabiana GianPaolo
sh-2.05$ awk '( $2!~/^B/ && ($3~/o$/ || $3~/-$/ ) ) { print }' elenco
m.1) Sechi Paolo
m.8) Fabiana GianPaolo


Esempio g.3) Estraggo le linee che hanno un cognome di almeno 8 lettere
sh-2.05$ awk '( length($2)>=8 ) { print "Cognome: " $2 "\n" "Nome: " $3 }' elenco
Cognome: Paolotto
Nome: Mario*

 

E' possibile digitare direttamente sia il testo da analizzare che lo script in awk . Nel caso sia necessario scrivere il programma awk su più linee basta inserire in fondo ad ogni linea il carattere \.  Quando ho, nella stessa sezione (porzione di programma delimitato da {}), più istruzioni le devo separare con ;.
Usando infine la funzione di ridirezione "here document" così caratterizzata <<marcatore (tipicamente si usa come marcatore il nome EOF)  posso ridirigere verso lo standard input di un comando del testo digitato direttamente su più righe semplicemente scrivendolo  tra due occorrenze di marcatore (l'ultima occorrenza deve apparire su una linea da sola).

Esempio g.4) Esempio di digitazione diretta sia del testo da analizzare che dello script
in awk distribuito su più linee
sh-2.05$ awk 'BEGIN { f=0;m=0;n=0 } \
{ n++;if (/^f./) { f++;print n"/"f " - (F) " $2 " " $3 } \
else if (/^m./) {m++ ; print n"/"m " - (M) " $3 " " $2 } } \
END {print "------ Fine" }' << EOF
m.1) Sechi Paolo
f.2) Rosi Bruna$
m.3) Bianchi Pierpaolo
m.4) Sechi Giulio^

m.5) Paolotto Mario*
f.6) Bronchi Rosa-
f.7) Fiini Marcella*
m.8) Fabiana GianPaolo
EOF
1/1 - (M) Paolo Sechi
2/1 - (F) Rosi Bruna$
3/2 - (M) Pierpaolo Bianchi
4/3 - (M) Giulio^ Sechi
6/4 - (M) Mario* Paolotto
7/2 - (F) Bronchi Rosa-
8/3 - (F) Fiini Marcella*
9/5 - (M) GianPaolo Fabiana
------ Fine

Concludiamo con un lungo script in linguaggio awk. Il programma mostrato, per certi versi assurdo, ha come obbiettivo quello di illustrare l'uso dei vari costrutti, delle variabili predefinite  e delle principali funzioni disponibili in awk

Nell'analisi dello script si tenga presente che in generale un prg awk è composto da queste sezioni (tutte facoltative):

BEGIN {
   istruzioni eseguite all'inizio prima della lettura del file
}
PATTERN1 {
   istruzioni eseguite per ogni linea che soddisfa il pattern1
}
PATTERN2 {
   istruzioni eseguite per ogni linea che soddisfa il pattern2
}
...
{
   istruzioni eseguite per ogni linea del file analizzata
}
END {
   istruzioni eseguite al termine della lettura del file
}

File Test.awk
#!/bin/awk -f
# può essere eseguito con:
#  ./test.awk <nomefile>
# oppure
# awk -f test.awk <nomefile>
# ------------------------------------------
# ELENCO FUNZIONI MATEMATICHE
# ------------------------------------------
# int(x)     : Restituisce il valore intero (troncato ovvero int(1.7)=1) di x.
# sqrt(x)    : Restituisce la radice quadrata di x.
# exp(x)     : Restituisce la potenza di x.
# log(x)     : Restituisce il logaritmo di x.
# sin(x)     : Restituisce il seno di x, pero' x deve essere espresso in radianti.
# cos(x)     : Restituisce il coseno di x, pero' x deve essere espresso in radianti.
# atan2(x/y) : Restituisce l'arco tngente di x/y, espresso in radianti.
# rand()     : Da' un numero casuale tra zero e uno, ma mai zero.
# ------------------------------------------
# ELENCO FUNZIONI STRINGA
# ------------------------------------------
# index(frase, parola)    : Restituisce la posizione del carattere dove comincia la parola 
#                           nella frase data - il conteggio parte da 1 e se la parola non è presente
#                           dentro la frase restituisce 0
# match(frase, regexp)    : match() somiglia abbastanza a index(), eccetto per il fatto che, invece 
#                           di cercare una sottostringa, cerca una espressione regolare. La funzione 
#                           match restituisce la posizione iniziale del match (corrispondenza), o zero
#                           se non c'è alcun match. Oltre a questo, match() cambia il valore di due 
#                           variabili chiamate RSTART e RLENGTH. RSTART contiene il valore di ritorno
#                           (posizione del primo match) e RLENGTH specifica la sua lunghezza in caratteri
#                           (o -1 se nessun match è stato trovato). Usando RSTART, RLENGTH, substr() e un 
#                           piccolo ciclo è possibile scorrere i vari match che compaiono nella vostra 
#                           stringa. 
# sub(regexp,newstr,str)  : Sub() troverà la prima sequenza di caratteri in str che corrisponde all'espressione 
#                           regolare regexp, e sostituirà la prima occorrenza di tale sequenza con la stringa 
#                           newstr. 
# gsub(regexp,newstr,str) : sub() e gsub() hanno gli stessi argomenti; l'unica cosa in cui sono diverse è
#                           che sub() sostituirà solo la prima corrispondenza di regexp (se esiste), e 
#                           gsub() invece effettuerà una sostituzione globale, cambiando tutte le 
#                           corrispondenze di regexp nella stringa. 
# length(stringa)         : Restituisce la lunghezza della stringa. Se non mettete nulla 
#                           nelle parentesi vi restistuisce la lunghezza della stringa $0.
# toupper(stringa)        : Converte la stringa in caratteri tutti maiuscoli.
# tolower(stringa)        : Converte la stringa in caratteri tutti minuscoli.
# split(stringa, matrice, (separatore)) : Divide una stringa in pezzi delimitati dal separatore 
#                           (se non indicato si intende lo spazio), e li mette nella matrice indicata
#                           come penultimo argomento.
#                           esempio: NrMesi=split("Gen,Feb,Mar,Apr,Mag,Giu,Lug,Ago,Set,Ott,Nov,Dic",Mesi,",")
# ------------------------------------------
# ELENCO FUNZIONI TEMPORALI
# ------------------------------------------
# systime()      : Restituisce il tempo timestamp, cioe' in secondi passati 
#                  dal 1/1/1970 alle 00:00:00 UTC.
# mktime("data") : Restituisce la data nel formato timestamp formato la data 
#                  deve essere scritta nel seguente formato: YYYY MM DD HH mm SS.
# strftime((FORMATO),(TIMESTAMP)) :
#                 Senza TIMESTAMP restituisce la data attuale nel formato Sun Nov 
#                 14 13:44:57 CET 2004 se non è specificato il FORMATO altrimenti secondo 
#                 lo stile indicato. 
#                 Se viene indicato un timestamp verra' indicata, nel formato 
#                 indicato, quella data. Il formato si specifica nel seguente modo:
#
# %a : Giorno della settimana abbreviato.
# %A : Giorno della settimana.
# %b : Mese dell'anno abbreviato.
# %B : Mese dell'anno.
# %c : Data nel formato predefinito.
# %C : Il secolo.
# %d : Il giorno del mese in formato numerico (01-31).
# %D : La data nel formato `%m/%d/%y.
# %e : Il giorno del mese in formato numerico ma con uno spazio al posto dello zero davanti ( 1-31).
# %F : La data in formato ISO-8601, cioe' %Y-%m-%d.
# %G : La settimana dell'anno secondo lo standard ISO.
# %h : E' uguale a %b.
# %H : L'ora nel formato 00-23.
# %I : L'ora nel formato 01-12.
# %j : Il giorno dell'anno (001-366).
# %m : Il mese in formato numerico.
# %M : I minuti (00-59).
# %n : A capo (carattere ASCII LF).
# %p : La scritta AM/PM riguardante l'ora.
# %r : L'ora in formato da 12, equivalente a %I:%M:%S %p.
# %R : L'ora equivalente a %H:%M.
# %S : I secondi.
# %t : Il carattere TAB.
# %T : L'ora nel formato %H:%M:%S.
# %u : Il giorno della settimana nel formato numerico (1-7) considerando lunedi' 1.
# %U : La settimana dell'anno considerando la domenica come primo giorno della settimana.(00-53)
# %V : La settimana dell'anno considerando il lunedi' come primo giorno della settimana.(01-53)
# %w : Il giorno della settimana in formato numerico, considerando la domenica come primo giorno della settimana.
# %W : La settimana dell'anno considerando il lunedi' come primo giorno della settimana.(00-53)
# %y : Le ultime due cifre dell'anno.
# %Y : L'anno con quattro cifre.
# %z : Il fuso orario nella forma +HHmm
# %Z : Il fuso orario con una sigla.
# %% Per ottenere il simolo percentuale %.
# ------------------------------------------
# ESEMPIO DI DICHIARAZIONE DI FUNZIONE
# ------------------------------------------
function print_sesso(sigla,progressivo)
{
   if (sigla=="m")
       printf("\tCampo 1: %d^ persona (maschio)\n",progressivo)
   else
       printf("\tCampo 1: %d^ persona (femmina)\n",progressivo)
}
# ----------------------------------
# Sezione eseguita all'inizio
# ----------------------------------
BEGIN {
# ------------------------------------------
# ESEMPIO USO VARIABILI AWK
# ------------------------------------------
   print "----- INIZIO ----------------------------"
   print "Delimitatore campo di input   (FS)  : " FS
   print "Delimitatore campo di output  (OFS) : " OFS
   print "Delimitatore record di input  (RS)  : " RS
   print "Delimitatore record di output (ORS) : " ORS
   print "Formato di output dei numeri  (OFMT): " OFMT
   nrA=0
}
# Sezione eseguita per ogni riga che soddisfa il pattern /Sechi/
/Sechi/ { print "********* PATTERN /SECHI/ SODDISFATTO  **********" }
# Sezione eseguita per ogni riga rielaborata
{
   printf("-----%2d^ ITERAZIONE ------------------------\n",NR)
   print "--> File(FILENAME) - Nr.Campi(NF): " FILENAME " - "  NF
   if ($0=="") next # proseguo con il ciclo successivo
   # estraggo il nominativo
   nrA++
   nrCol=split($0,Arr,")") # split in un Array di una stringa
   Nominativo[nrA]=Arr[nrCol]
   # estraggo sesso ed il progressivo
   posParentesi=length($1)
   posPunto=index($1,".")
   progressivo=int(substr($1,posPunto+1,posParentesi-posPunto-1))
   sigla=substr($1,1,1)
   print "Riga analizzata: " $0
   print_sesso(sigla,progressivo)
   for (i=2 ; i <= NF ; i++)
   {
       if (match($i,/Sechi/) > 0)
       {
          sub(/Sechi/,"*****",$i) # restituisce 0 se non sostituisce
          printf("\tCampo %d: %s\n",i,$i)
       }
       else
          printf("\tCampo %d: %s\n",i,toupper($i))
   }
}
# Sezione eseguita alla fine
END {
   print "----- FINE ------------------------------"
   print "Nr righe nel file (FNR): " FNR
   print "Nr righe analizzate: " nrA
   NomiLetti=""
   i=0
   srand()
   # esiste anche while (condizione=vera) { istruzioni } 
   do
   {
      i++
      if (i % 2 ==0) continue # salto i pari
      eta=int(rand()*30)+1 # numero casuale tra 1 e 30
      # esempio di concatenazione di stringhe
      NomiLetti=NomiLetti i ")" Nominativo[i] " - eta: " eta "\n"
      
   } while (i < nrA)
   # Potevo usare anche questa formula di for particolare.
   # ATTENZIONE! L'elenco non risulta ordinato poichè dipende dalla gestione di awk
   for (i in Nominativo)
   {
      if (i % 2 ==0) continue # salto i pari
      eta=int(rand()*30)+1 # numero casuale tra 1 e 30
      NomiLetti=NomiLetti i ")" Nominativo[i] " - eta: " eta "\n"
   }
   # elimino  l'intero array
   delete Nominativo
   printf("%s",NomiLetti)
   print strftime("%A, %e %B %Y%n%T",systime())
}

Che produce questo output

Esempio 5) Esegue lo script test.awk
sh-2.05$ awk -f test.awk elenco
----- INIZIO ----------------------------
Separatore campo di input   (FS)  :
Separatore campo di output  (OFS) :
Separatore record di input  (RS)  :

Separatore record di output (ORS) :

Formato di output dei numeri(OFMT): %.6g
----- 1^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: m.1) Sechi Paolo
        Campo 1: 1^ persona (maschio)
        Campo 2: SECHI
        Campo 3: PAOLO
----- 2^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: f.2) Rosi Bruna$
        Campo 1: 2^ persona (femmina)
        Campo 2: ROSI
        Campo 3: BRUNA$
----- 3^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: m.3) Bianchi Pierpaolo
        Campo 1: 3^ persona (maschio)
        Campo 2: BIANCHI
        Campo 3: PIERPAOLO
----- 4^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: m.4) Sechi Giulio^
        Campo 1: 4^ persona (maschio)
        Campo 2: SECHI
        Campo 3: GIULIO^
----- 5^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 0
----- 6^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: m.5) Paolotto Mario*
        Campo 1: 5^ persona (maschio)
        Campo 2: PAOLOTTO
        Campo 3: MARIO*
----- 7^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: f.6) Bronchi Rosa-
        Campo 1: 6^ persona (femmina)
        Campo 2: BRONCHI
        Campo 3: ROSA-
----- 8^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: f.7) Fiini Marcella*
        Campo 1: 7^ persona (femmina)
        Campo 2: FIINI
        Campo 3: MARCELLA*
----- 9^ ITERAZIONE ------------------------
--> File(FILENAME) - Nr.Campi(NF): elenco - 3
Riga analizzata: m.8) Fabiana GianPaolo
        Campo 1: 8^ persona (maschio)
        Campo 2: FABIANA
        Campo 3: GIANPAOLO
----- FINE ------------------------------
Nr righe nel file (FNR): 9
Nr righe analizzate: 8
1) Sechi Paolo - eta: 23
2) Rosi Bruna$ - eta: 23
3) Bianchi Pierpaolo - eta: 11
4) Sechi Giulio^ - eta: 2
5) Paolotto Mario* - eta: 15
6) Bronchi Rosa- - eta: 3
7) Fiini Marcella* - eta: 17
8) Fabiana GianPaolo - eta: 17
Wednesday, 28 April 2010
21:03:50

 

LINK - collegamenti


Elenchiamo qui alcuni link utili:

http://www.pluto.it/files/ildp/guide/abs/index.html (ITA)
http://dida.fauser.edu/info/cshell/cshell.htm (ITA)
http://www.freebsd.org/cgi/man.cgi (ENG)
http://www.grymoire.com/Unix/Sh.html (ENG)
http://www.grymoire.com/Unix/Csh.html (ENG)
http://www.grymoire.com/Unix/Awk.html (ENG)
http://tldp.org/LDP/abs/html/awk.html (ENG)
http://www.network-theory.co.uk/docs/bashref/index.html (ENG)
http://docstore.mik.ua/orelly/unix/ksh/index/idx_0.htm (ENG)
http://www.faqs.org/faqs/unix-faq/shell/csh-whynot/ (ENG)
http://tldp.org/LDP/Bash-Beginners-Guide/html/ (ENG)
http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html (ENG)
http://www.intuitive.com/wicked/table-of-contents.shtml (ENG)
http://www.staff.tugraz.at/reinfried.o.peter/unix/ (ENG)