La shell di Linux
 Linux ha una varietà di shell differenti:
 Bourne shell (sh), C shell (csh), Korn shell (ksh), TC
shell (tcsh), Bourne Again shell (bash).
 Sicuramente la shell più popolare è “bash”. Bash è
la shell che è stata introdotta nel sistema operativo
GNU. Bash è una shell sh-compatibile che
incorpora utili caratteristiche della Korn shell (ksh)
e della C shell (csh).
 Bash è conforme allo standard IEEE POSIX
P1003.2/ISO 9945.2 Shell and Tools.
 Garantisce miglioramenti funzionali rispetto a sh
sia per programmare che per l’uso interattivo.
1.1
Programming o Scripting ?
 Bash non è soltanto un’eccellente interprete di comandi, ma
un vero e proprio supporto per un linguaggio di script. Il
linguaggio di script della shell permette di utilizzare le
potenzialità della shell e di automatizzare un mucchio di
compiti che richiederebbero altrimenti una grande quantità
di comandi.
 Differenza tra linguaggi di programmazione e linguaggi di
script:
 I linguaggi di programmazione sono in genere notevolmente più
potenti e veloci dei linguaggi di script. I linguaggi di programmazione
generalmente partono dal codice sorgente e vengono compilati in un
eseguibile. Tale eseguibile non è facilmente portabile su differenti
sistemi operativi.
 Un linguaggio di script parte a sua volta dal codice sorgente, ma non
viene compilato in un eseguibile. Piuttosto, un interprete legge le
istruzioni nel file sorgente ed esegue ciascuna istruzione. I
programmi interpretati sono in genere più lenti dei programmi
compilati. Il vantaggio principale è che si può facilmente portare il file
sorgente su ogni sistema operativo. Bash è un linguaggio di script.
Altri esempio di linguaggi di script sono Perl, Lisp, e Tcl.
1.2
Il primo programma bash
 Occorre sapere come usare un editor di testo. Ci
sono due editor di testo principali in Linux:
 vi (o vim), emacs (o xemacs).
 Dunque lanciamo un editor di testo; ad esempio:
 $ emacs &
e digitiamo ciò che segue:
 #!/bin/bash
echo “Hello World”
 La prima linea dice a Linux di utilizzare l’interprete
bash per eseguire questo script. Lo chiameremo
hello.sh. Ora rendiamo lo script eseguibile:
 $ chmod 700 hello.sh
 $ ls –l
-rwx------ hello.sh
1.3
Il primo programma bash
 Per eseguire il programma:
 $ hello.sh
-bash: hello.sh: command not found
La directory home (in cui il comando hello.sh si trova)
non si trova fra quelle elencate nella variabile d’ambiente PATH
 echo $PATH
:bin:/usr/bin:…
Occorre specificare il path di hello.sh
 $/home/lferrari/Scripts/hello.sh
 $./hello.sh
1.4
Il secondo programma bash
 Scriviamo un programma che copia tutti i files in una
directory, e poi cancella la directory con tutto il suo
contenuto. Ciò può essere fatto col seguente comando:
 $ mkdir trash
$ cp * trash
$ rm -rf trash
 Invece di essere costretti a digitare tutto ciò in modo
interattivo nella shell, scriviamo uno script di shell:
 $ cat > trash
#!/bin/bash
# this script deletes some files
mkdir trash
cp * trash
rm -rf trash
echo “Deleted all files!”
1.5
Variabili
 Si possono usare le variabili come in ogni linguaggio di
programmazione. I loro valori sono sempre memorizzati
come stringhe, ma ci sono delle strutture nel linguaggio di
shell che convertono le variabili in numeri per fare i calcoli.
 Non c’è bisogno di dichiarare una variabile, il solo fatto di
assegnarle un valore la crea automaticamente.
 Esempio
 #!/bin/bash
STR=“Hello World!”
echo $STR
 La linea 2 crea una variabile detta STR e assegna la stringa
"Hello World!" ad essa. Quindi il valore di questa variabile
viene recuperato mettendo il '$' all’inizio.
1.6
Attenzione!
 Il linguaggio della shell non tipizza (assegna un tipo a) le
variabili. Ciò significa che una variabile può contenere dati
numerici o alfabetici.
 count=0
 count=Sunday
 Cambiare il tipo di una variabile può generare confusione,
sia per chi scrive lo script che per qualcuno che cerca di
modificarlo, pertanto è raccomandato l’uso di un solo tipo di
dati per ogni singola variabile all’interno di uno script.
 \ è il carattere di escape di bash, e conserva il valore
letterale del carattere che segue.
 $ ls \*
ls: *: No such file or directory
1.7
Virgolette singole e doppie
 Quando si assegnano dati contenenti spazi o caratteri speciali, i dati
devono essere racchiusi tra virgolette (singole o doppie).
 Quando si usano virgolette doppie (partial quoting) per stampare a
video una stringa di caratteri, ogni variabile verrà sostituita dal valore
che essa assume in quel momento.
 $ var=“test string”
$ newvar=“Value of var is $var”
$ echo $newvar
Value of var is test string
 L’uso di virgolette singole (full quoting) per visualizzare una stringa di
caratteri non permette la risoluzione delle variabili.
 $ var=’test string’
$ newvar=’Value of var is $var’
$ echo $newvar
Value of var is $var
1.8
Esempi vari
 $ pippo= pluto
 $ pippo =pluto
 $ ls [Pp]*
 $ ls “[Pp]*”
 $ ls ‘[Pp]*’




$
$
$
$
errore
Not resolved
var=“’(]\\{}\$\””
echo $var
echo “$var”
echo ‘$var’
 $ echo \z
 $ echo \\z
 $ echo ‘\\z’
# z
# \z
# \\z
1.9
Esempi vari
$ pippo=cat
$ echo “comando = \” $pippo \” “
comando =“ cat ”
$ echo ‘comando = \” $pippo \” ‘
comando =\” $pippo \”
$ echo ‘comando = ” $pippo ” ‘
comando =” $pippo “
1.10
Il comando export
 Il comando export inserisce una variabile nell’ambiente, così che essa sarà
accessibile ai processi figli. Per esempio:

$ x=hello
$ bash # Run a child shell.
$ echo $x
# Nothing in x.
$ exit
# Return to parent.
$ export x
$ bash
$ echo $x
hello
# It's there.
 Se il processo figlio modifica x, non verrà modificato il valore originale nel
processo padre. Si verifichi tale fatto cambiando x nel modo seguente:
 $ x=ciao
$ exit
$ echo $x
hello
1.11
Variabili di ambiente
 Ci sono due tipi di variabili:
 Variabili locali
 Variabili di ambiente
 Le variabili di ambiente sono definite a livello di sistema e
possono di norma essere reperite usando il comando env.
Le variabili di ambiente contengono valori speciali. Ad
esempio,
 $ echo $SHELL
/bin/bash
$ echo $PATH
/usr/X11R6/bin:/usr/local/bin:/bin:/usr/bin
 Le variabili di ambiente sono definite in /etc/profile,
/etc/profile.d/ e ~/.bash_profile. Questi files sono files di
inizializzazione e vengono letti quando la shell bash viene
invocata. Al momento del logout da parte dell’utente, bash
legge ~/.bash_logout.
1.12
Variabili di ambiente
Il pathname della home dell’utente
(l’argomento di default di cd).
 PATH: Il “search path” per i comandi. É un elenco
delle directories (separate da :) nelle quali la shell
cerca il comando da eseguire.
 Solitamente, si lancia uno script nel modo
seguente:
 HOME:
 $ ./trash.sh
 Ponendo
PATH=$PATH:. la nostra working
directory viene inclusa nel “search path” per i
comandi, e possiamo digitare semplicemente:
 $ trash.sh
1.13
Variabili di ambiente




LOGNAME: contiene il nome dell’utente.
HOSTNAME: contiene il nome della macchina.
MACHTYPE: hardware del sistema.
PS1: sequenza di caratteri visualizzata davanti al prompt.
 \t ora
 \d data
 \w current directory
 \W ultima parte della current directory
 \u nome dell’utente
 \$ carattere del prompt
Esempio
[[email protected] lferrari]$ PS1=‘ciao \u *’
ciao lferrari* _
 UID: contiene l’id number dell’utente (non può essere cambiato).
1.14
Il comando exit
 Il comando exit può essere usato per terminare
uno script. Esso restituisce un valore, che è
disponibile per il processo padre dello script.
 Quando uno script termina col comando exit senza
ulteriori parametri, lo stato di uscita è lo stato di uscita
dell’ultimo comando eseguito nello script.
#!/bin/bash
#!/bin/bash
COMMAND_1
COMMAND_1
. . .
. . .
# exit with status of last command.
COMMAND_LAST
# exit with status of last command.
COMMAND_LAST
exit
exit $?
1.15
Il comando exit
 Il comando exit può essere usato per terminare
uno script. Esso restituisce un valore, che è
disponibile per il processo padre dello script.
 Quando uno script termina con
exit nnn
dove nnn=0-255, lo stato di uscita è nnn.
1.16
Il Comando read
 Il comando read consente di leggere l’input da tastiera e
memorizzarlo in una variabile.
 Esempio (read.sh)
 #!/bin/bash
echo -n “Enter name of file to delete: ”
read file
echo “Type 'y' to remove it, 'n' to change your
mind ... ”
rm -i $file
echo "That was YOUR decision!"
 La linea 3 crea una variabile chiamata file e le assegna
l’input da tastiera. Quindi il valore di questa variabile viene
richiamato inserendo il simbolo ‘$’ davanti ad essa.
1.17
Il Comando read
 Opzioni
 read –s
(non stampa a video l’input)
 read –nN (accetta soltanto N caratteri di input)
 read –p “message” (visualizza il messaggio “message”)
 read –tT (accetta l’input per T secondi)
 Esempio
$ read –s –n1 -p “Yes (Y) or not (N)?” answer
Yes (Y) or not (N) ? (digitare Y: non si vedrà)
$ echo $answer
Y
1.18
Sostituzione dei comandi
 La “virgoletta retrograda”, o accento grave, “`” è diversa
dalla virgoletta “´”. Essa viene usata per la sostituzione dei
comandi: `command`
 $ LIST=`ls`
$ echo $LIST
hello.sh read.sh
 PS1=“`pwd`>”
/home/rinaldi/didattica/>
 Possiamo eseguire la sostituzione dei comandi anche
usando $(command)
 $ LIST=$(ls)
$ echo $LIST
hello.sh read.sh
 rm $( find / -name “*.tmp” )
 ls $( pwd )
 ls $( echo /bin )
1.19
Esempio
$ a=`echo Hello`
$ echo $a
$ echo ‘$a’
$
$
$
$
b=`ls /home`
echo $b
echo $a $b
echo “$a $b”
1.20
Operatori aritmetici
+
*
/
**
%
somma
sottrazione
moltiplicazione
divisione
esponenziazione
modulo
Esempio
$
$
$
$
a=/(5+2/)*3
echo $a
b=2**3
echo $a+$b
1.21
Valutazioni aritmetiche
 Il costrutto let può essere usato per creare funzioni
matematiche
 $ let X=10+2*7
$ echo $X
24
$ let Y=X+2*4
$ echo $Y
32
Non è necessario usare $X
per riferirsi al valore di X
 Un’espressione aritmetica può essere valutata usando
$[expression] o $((expression))
 $ echo $((123+20))




143
$ VALORE=$[123+20]
$ echo $[10*$VALORE]
1430
$ echo $[2**3]
$ echo $[8%3]
1.22
Valutazioni aritmetiche
 Esempio (operations.sh)
 #!/bin/bash
echo -n “Enter the first number: ”; read x
echo -n “Enter the second number: ”; read y
add=$(($x + $y))
sub=$(($x - $y))
mul=$(($x * $y))
div=$(($x / $y))
mod=$(($x % $y))
# print out the answers:
echo “Sum: $add”
echo “Difference: $sub”
echo “Product: $mul”
echo “Quotient: $div”
echo “Remainder: $mod”
1.23
Costrutti condizionali
 Le strutture condizionali ci permettono di decidere
se eseguire un’azione oppure no, a seconda del
valore di un’espressione da valutare. Il costrutto
condizionale fondamentale è:
 if [ expression ];
then
statements
elif [ expression ];
then
statements
else
statements
fi
 Le parti elif (else if) e else sono opzionali.
1.24
Espressioni
 Un’espressione può essere: un confronto fra stringhe, un
confronto fra numeri, un operatore su file, un operatore
logico ed è rappresentata da [ expression ]:
 Confronto fra stringhe:
 =
 !=
 -n
 -z
per valutare se due stringhe sono uguali
per valutare se due stringhe sono diverse
per valutare se la lunghezza di una stringa è maggiore di
zero
per valutare se la lunghezza di una stringa è uguale a zero
 Esempi:




[ s1 = s2 ]
(vero se s1 è uguale a s2, altrimenti falso)
[ s1 != s2 ] (vero se s1 è diverso da s2, altrimenti falso)
[ s1 ]
(vero se s1 è non vuota, altrimenti falso)
[ -n s1 ]
(vero se s1 ha lunghezza maggiore di 0, altrimenti
falso)
 [ -z s2 ]
(vero se s2 ha lunghezza 0, altrimenti falso)
1.25
Espressioni
 Confronto fra numeri:






-eq
-ge
-le
-ne
-gt
-lt
valuta se due numeri sono uguali
valuta se un numero è maggiore o uguale a un altro
valuta se un numero è minore o uguale a un altro
valuta se due numeri sono diversi
valuta se un numero è maggiore di un altro
valuta se un numero è minore di un altro
 Esempi:
 [ n1 -eq n2 ] (vero se n1 è uguale a n2, altrimenti falso)
 [ n1 -ge n2 ] (vero se n1 è maggiore o uguale a n2, altrimenti




falso)
[ n1 -le n2 ] (vero se n1 è minore o uguale a n2, altrimenti
falso)
[ n1 -ne n2 ] (vero se n1 è diverso da n2, altrimenti falso)
[ n1 -gt n2 ] (vero se n1 è maggiore di n2, altrimenti falso)
[ n1 -lt n2 ] (vero se n1 è minore di n2, altrimenti falso)
1.26
 #!/bin/bash # if0.sh
echo -n “Enter your login name: "
read name
if [ “$name” = “$LOGNAME” ];
then
echo “Hello, $name. How are you today ?”
else
echo “You are not $LOGNAME, so who are you ?”
fi
 #!/bin/bash # if1.sh
echo -n “Enter a number 1 < x < 10: "
read num
if [ “$num” -lt 10 ]; then
if [ “$num” -gt 1 ]; then
echo “$num*$num=$(($num*$num))”
else
echo “Wrong insertion !”
fi
else
echo “Wrong insertion !”
fi
1.27
Espressioni
 Operatori su files:
 -d
controlla se il cammino dato rappresenta una directory
 -f
controlla se il cammino dato rappresenta un file ordinario
 -e
controlla se il nome del file esiste
 -s
controlla se un file ha lunghezza maggiore di 0
 -r
controlla se si possiede il permesso di lettura per un file o una directory
 -w
controlla se si possiede il permesso di scrittura per un file o una directory
 -x
controlla se si possiede il permesso di esecuzione per un file o una
directory
 Esempi:
 [ -d fname ]
(vero se fname è una directory, altrimenti falso)
 [ -f fname ]
(vero se fname è un file ordinario, altrimenti falso)
 [ -e fname ]
(vero se fname esiste, altrimenti falso)
 [ -s fname ]
(vero se la lunghezza di fname è maggiore di 0, altrimenti
falso)
 [ -r fname ]
(vero se fname ha il permesso di lettura, altrimenti falso)
 [ -w fname ]
(vero se fname ha il permesso di scrittura, altrimenti falso)
 [ -x fname ]
(vero se fname ha il permesso di esecuzione, altrimenti falso)
1.28
Esempio
 #!/bin/bash
if [ -f /etc/fstab ];
then
cp /etc/fstab .
echo “Done.”
else
echo “This file does not exist.”
exit 1
fi
 Esercizio. Scrivere uno script di shell che prenda in input un
nome di file <pippo>:
 Lo script controlla se esiste una cartella Backup, altrimenti la crea.
 Se il file cercato esiste, copia il file con lo stesso nome + .bak nella
cartella Backup.
 Se il file non esiste, allora visualizza il messaggio “The file <pippo>
does not exist!!!”
1.29
Soluzione
#!/bin/bash
if [ ! –d ./Backup ]
then mkdir ./Backup
fi
read –p “insert the name of a file” pippo
if [ -f $pippo ]
then cp $pippo ./Backup/$pippo.bak
else echo “The file $pippo does not exist!!!”
fi
1.30
Espressioni
 Operatori logici:
!
negare (NOT) un’espressione logica
 -a congiunzione (AND) tra due espressioni logiche
 -o disgiunzione (OR) tra due espressioni logiche
 #!/bin/bash # if3.sh
echo -n “Enter a number 1 < x < 10:”
read num
if [ “$num” -gt 1 –a “$num” -lt 10 ];
then
echo “$num*$num=$(($num*$num))”
else
echo “Wrong insertion !”
fi
1.31
Espressioni
 Operatori logici:
 && congiunzione (AND) tra due espressioni logiche
 ||disgiunzione (OR) tra due espressioni logiche
 #!/bin/bash # if4.sh
echo -n "Enter a number 1 < x < 10: "
read num
if [ “$number” -gt 1 ] && [ “$number” -lt 10 ];
then
echo “$num*$num=$(($num*$num))”
else
echo “Wrong insertion !”
fi
1.32
Esempio
 $ cat iftrue.sh # iftrue.sh
#!/bin/bash
echo “Enter
if cd $x 2>
echo “I am
else
echo “The
exit 1
fi
a path: ”; read x
/dev/null then
in $x and it contains”; ls
directory $x does not exist”;
 $ ./iftrue.sh
Enter a path: /home
srinaldi afrosini riccardo …
 $ ./iftrue.sh
Enter a path: pippo
The directory pippo does not exist
1.33
Parametri di shell
 Un parametro posizionale corrisponde ad un argomento fornito alla shell
al momento dell’esecuzione di un programma. Il primo argomento
specificato corrisponde alla variabile 1, il secondo argomento alla
variabile 2 e così via. Il parametro posizionale “N” può essere
referenziato come “${N}”, o come “$N” quando “N” consiste in una
singola cifra.
 Parametri speciali
 $# è il numero dei parametri passati
 $0 rende il nome dello script di shell in esecuzione nonchè la sua posizione
nel file system
 $* rende una singola parola contenente tutti i parametri passati allo script
 [email protected] rende un array di parole contenente tutti i parametri passati allo script

$ cat sparameters.sh ( sparameters.sh )
#!/bin/bash
echo “$#; $0; $1; $2; $*; [email protected]”
$ sparameters.sh alba chiara
2; ./sparameters.sh; alba; chiara; alba chiara; alba chiara
1.34
Trash
 $ cat trash.sh ( trash.sh )
#!/bin/bash
if [ $# -eq 1 ];
then
if [ ! –d “$HOME/trash” ];
then
mkdir “$HOME/trash”
fi
mv $1 “$HOME/trash”
else
echo “Use: $0 filename”
exit 1
fi
1.35
Il costrutto case
 Viene usato per eseguire azioni basate su valori specifici.
Usato spesso al posto di un costrutto if se c’è un numero
elevato di condizioni.
 Il valore usato può essere un’espressione.
 Ciascun insieme di azioni deve concludersi con una coppia di punto
e virgola.
 Il simbolo *) viene usato per accettare
corrispondente con la lista dei valori.
 case $var in
val1)
statements;;
val2)
statements;;
*)
statements;;
esac
1.36
ogni
valore
non
Esempio
 #!/bin/bash
( case.sh )
echo -n “Enter a number 1 < x < 10: ”
read x
case $x in
2) echo “Value of x is 2.”;;
3) echo “Value of x is 3.”;;
4) echo “Value of x is 4.”;;
5) echo “Value of x is 5.”;;
6) echo “Value of x is 6.”;;
7) echo “Value of x is 7.”;;
8) echo “Value of x is 8.”;;
9) echo “Value of x is 9.”;;
1 | 10) echo “wrong number.”;;
*) echo “Unrecognized value.”;;
esac
1.37
Costrutti di iterazione
 Il ciclo for viene usato quando si vuole ripetere un blocco di
comandi in corrispondenza di valori tratti da un elenco.
 for var in list
do
istruzioni
done
 Le istruzioni vengono eseguite assegnando a var ciascun
valore in list.
 #!/bin/bash
let sum=0
for num in 1 2 3 4 5
do
let sum=$sum+$num
done
echo $sum
1.38
Costrutti di iterazione: <list>
 #!/bin/bash
for x in paper pencil pen; do
echo “The value of variable x is: $x”
sleep 1
done
# The value of variable x is paper
# The value of variable x is pencil
# The value of variable x is pen
 #!/bin/bash
for x in “paper A4” “pencil STADTLER” “pen BIC”; do
echo “The value of variable x is: $x”
sleep 1
done
# The value of variable x is paper A4
# The value of variable x is pencil STADTLER
# The value of variable x is pen BIC
1.39
Costrutti di iterazione: <list>
 #!/bin/bash
lista=“antonio
michele
paolo
luca”
for x in $lista
do
echo “The value of variable x is: $x”
sleep 1
done
#
#
#
#
The
The
The
The
value
value
value
value
of
of
of
of
variable
variable
variable
variable
x
x
x
x
is
is
is
is
1.40
antonio
michele
paolo
luca
Costrutti di iterazione: <list>
 #!/bin/bash
for x in *
do
ls -l “$x”
sleep 1
done
# Lists all files in current directory
 #!/bin/bash
for x in /bin
do
ls -l “$x”
done
# Lists all files in /bin
1.41
Costrutti di iterazione: <list>
#!/bin/bash
read –p “Insert the name of a directory” directory
echo "symbolic links in directory \” $directory \” "
for file in $( find $directory -type l ) # -type l = symbolic links
do
echo "$file"
done | sort
# Otherwise file list is unsorted
1.42
Costrutti di iterazione: <list>
 Se la parte list viene omessa, a var viene assegnato
ciascun parametro passato allo script ( $1, $2, $3,…).
1.43
Costrutti di iterazione: <list>
 Se la parte list viene omessa, a var viene assegnato
ciascun parametro passato allo script ( $1, $2, $3,…).
 $ cat for1.sh ( for1.sh )
#!/bin/bash
for x
do
echo “The value of variable x is: $x”
sleep 1
done
$ for1.sh alba chiara
The value of variable x is: alba
The value of variable x is: chiara
1.44
Esempio 1
#!/bin/bash ( old.sh )
# Move the command line arg files to old directory.
if [ $# -eq 0 ] #check for command line arguments
then
echo “Usage: $0 file …”
exit 1
fi
if [ ! –d “$HOME/old” ]
then
mkdir “$HOME/old”
fi
echo The following files will be saved in the old directory:
echo $*
for p in $* #loop through all command line arguments
do
mv $p “$HOME/old/”
chmod 400 “$HOME/old/$p”
done
ls -l “$HOME/old”
1.45
Esempio 2
#!/bin/bash ( args.sh )
# Invoke this script with several arguments: “one two three“
if [ ! -n “$1” ]; then
echo “Usage: $0 arg1 arg2 ..." ; exit 1
fi
echo ; index=1 ;
echo “Listing args \”\$*\”:”
for arg in “$*” ; do
echo “Arg $index = $arg”
let “index+=1” # increase variable index by one
done
echo “Entire arg list seen as single word.”
echo ; index=1 ;
echo “Listing args with \”\[email protected]\”:”
for arg in “[email protected]” ; do
echo “Arg $index = $arg”
let “index+=1”
done
echo “Arg list seen as separate words.”
1.46
Operazioni sulle variabili
……
let “index += 5”
#incrementa
#index di 5
……
+=#incrementa la variabile
-= #decrementa la variabile
*= #moltiplica la variabile
/= #divide la variabile
1.47
la
variabile
Arrays
 Nella shell bash, possiamo usare gli arrays. Il modo
più semplice per crearne uno è usare uno dei due
metodi seguenti:
 pet[0]=dog
pet[1]=cat
pet[2]=fish
pet[4]=apple
 pet=( dog cat fish apple )
 Si possono inserire fino a 1024 elementi. Per
estrarre un valore, digitare ${arrayname[i]}.
 $ echo ${pet[0]}
dog
 $ echo ${pet[2]}
fish
1.48
Arrays
 Per estrarre tutti gli elementi, usare un asterisco
come segue:
echo ${arraynames[*]}
 Per vedere quanti elementi ci sono nell’array:
echo ${#arraynames[*]}
 Si possono utilizzare gli arrays nei cicli per
realizzare la lista di un ciclo for:
 for x in ${arrayname[*]}
do
echo $x
done
1.49
Un “for” alternativo
 Una forma alternativa per la struttura for è
 for (( EXPR1 ; EXPR2 ; EXPR3 ))
do
istruzioni
done
 Per prima cosa, viene valutata l’espressione aritmetica
EXPR1. Quindi EXPR2 viene valutata ripetutamente fino a
che non assume risulta falsa. Ogni volta che EXPR2 è vera,
le istruzioni vengono eseguite e, in coda, viene eseguita
EXPR3.
1.50
Un “for” alternativo
 Una forma alternativa per la struttura for è
 for (( EXPR1 ; EXPR2 ; EXPR3 ))
do
istruzioni
done
 Per prima cosa, viene valutata l’espressione aritmetica
EXPR1. Quindi EXPR2 viene valutata ripetutamente fino a
che non assume risulta falsa. Ogni volta che EXPR2 è vera,
le istruzioni vengono eseguite e, in coda, viene eseguita
EXPR3.
 $ cat for2.sh
#!/bin/bash
echo –n “Enter a number: ”; read x
let sum=0
for (( i=1 ; $i<$x ; i=$i+1 )) ; do
let “sum = $sum + $i”
done
echo “the sum of the first $x numbers is: $sum”
1.51
Debugging
 Bash fornisce due opzioni che danno informazioni utili per il
debugging:
-v : visualizza ciascuna linea dello script così come era stata digitata
prima dell’esecuzione;
-x : simile a sopra, ma se c’è un ciclo mostra tutti le iterazioni.
 Utilizzo: #!/bin/bash –v, o #!/bin/bash –x
 $ cat for3.sh
#!/bin/bash –x
echo –n “Enter a number: ”; read x
let sum=0
for (( i=0 ; $i<=$x ; i=$i+1 )) ; do
let “sum = $sum + $i”
done
echo “the sum of the first $x numbers is: $sum”
1.52
Debugging
$ ./for3.sh
+ echo –n ‘Enter a number: ’
Enter a number: + read x
3
+ let sum=0
+ (( i=0 ))
+ (( 0<=3 ))
+ let ‘sum = 0 + 0’
+ (( i=0+1 ))
+ (( 1<=3 ))
+ let ‘sum = 0 + 1’
+ (( i=1+1 ))
+ (( 2<=3 ))
+ let ‘sum = 1 + 2’
+ (( i=2+1 ))
+ (( 3<=3 ))
+ let ‘sum = 3 + 3’
+ (( i=3+1 ))
+ (( 4<=3 ))
+ echo ‘the sum of the first 3 numbers is: 6’
the sum of the first 3 numbers is: 6
1.53
Dov’è l’errore?
 Uno script da correggere
#!/bin/bash
# ex74.sh
a=37
if [$a -gt 27 ]
then echo $a
fi
Output dello script:
./ex74.sh: [37: command not found
1.54
Dov’è l’errore?
 Uno script da correggere
#!/bin/bash -x
# ex74.sh
a=37
if [$a -gt 27 ]
then echo $a
fi exit
0
Output dello script:
+a=37
+’[37’ -gt 37 ‘]’
+ ./ex74.sh: [37: command not found
….
1.55
Il costrutto While
 La struttura while è una struttura di ciclo che viene usata
per eseguire un insieme di comandi finchè una certa
condizione specificata è vera. Il ciclo termina non appena la
condizione diventa falsa. Se la condizione non diventa mai
falsa, il ciclo non termina e si entra in loop.
 while [ espressione ]
do
istruzioni
done
1.56
Il costrutto While
 La struttura while è una struttura di ciclo che viene usata
per eseguire un insieme di comandi finchè una certa
condizione specificata è vera. Il ciclo termina non appena la
condizione diventa falsa. Se la condizione non diventa mai
falsa, il ciclo non termina e si entra in loop.
 while [ espressione ]
do
istruzioni
done
 $ cat while.sh ( while.sh )
#!/bin/bash
echo –n “Enter a number: ”; read x
let sum=0; let i=1
while [ $i –le $x ]; do
let “sum = $sum + $i”
let i=$i+1
done
1.57
echo “the sum of the first $x numbers is: $sum”
Menu
 #!/bin/bash
# menu.sh
clear ; loop=y
while [ “$loop” = y ] ;
do
echo “Menu”; echo “====”
echo “D: print the date”
echo “W: print the users who are currently log on.”
echo “P: print the working directory”
echo “Q: quit.”
echo
read –s choice
case $choice in
D | d) date ;;
W | w) who ;;
P | p) pwd ;;
Q | q) loop=n ;;
*) echo “Illegal choice.” ;;
esac
echo
done
1.58
Trovare un pattern e editarlo
$ cat grep_edit.sh
#!/bin/bash
# grep_edit.sh
# Edit argument files $2 ..., that contain pattern $1
if [ $# -le 1 ]
then
echo “Usage: $0 pattern file …” ; exit 1
else
pattern=$1 # Save original $1
shift # shift the positional parameter to the left by 1
while [ $# -gt 0 ] # New $1 is first filename
do
grep “$pattern” $1 > /dev/null
if [ $? -eq 0 ] ; then # If grep found pattern
vi $1 # then vi the file
fi
shift
done
fi
$ grep_edit.sh while ~
1.59
Il costrutto continue
 Il comando continue genera un salto all’iterazione
successiva del ciclo, dimenticando tutti i restanti comandi
in quella particolare iterazione.
 #!/bin/bash
LIMIT=19
echo
echo “Printing Numbers 1 through 20 (but not 3
and 11)”
a=0
while [ $a -le “$LIMIT” ]; do
a=$(($a+1))
if [ “$a” -eq 3 ] || [ “$a” -eq 11 ]
then
continue
fi
echo -n “$a ”
done
1.60
Il costrutto break
 Il comando break termina il ciclo (ed esce da esso).
 #!/bin/bash
LIMIT=19
echo “Printing Numbers 1 through
something happens after 2 … ”
a=0
while [ $a -le “$LIMIT” ]; do
a=$(($a+1))
if [ “$a” -gt 2 ]
then
break
fi
echo -n “$a ”
done
echo; echo; echo
1.61
20,
but
Il costrutto until
 La struttura until è molto simile alla struttura while. La
struttura until mantiene il ciclo finchè la condizione è falsa.
Quindi, sostanzialmente, il significato è “finchè la
condizione è falsa, fai questo”.
 until [espressione]
do
istruzioni
done
 $ cat countdown.sh
#!/bin/bash
#countdown.sh
echo “Enter a number: ”; read x
echo ; echo Count Down
until [ “$x” -le 0 ]; do
echo $x
x=$(($x–1))
sleep 1
done
echo ; echo GO !
1.62
Manipolare le stringhe
 Bash supporta un numero impressionante di operazioni per
manipolare stringhe. Sfortunatamente, questi strumenti non
soddisfano una delle primitive esigenze di Linux, cioè quella
dell’economicità dei comandi.
 ${#string} rende la lunghezza della stringa.
 ${string:position} estrae da $string la sottostringa che
inizia dalla posizione $position.
 ${string:position:length} estrae $length caratteri della
sottostringa di $string che inizia dalla posizione $position.
 Esempio:
 $ st=0123456789
$ echo ${#st}
10
$ echo ${st:6}
6789
$ echo ${st:6:2}
67
1.63
Sostituzione dei parametri
 Manipolare e/o espandere variabili:
 ${parameter-default}, se “parameter” non è istanziato, usa
“default”.
 $ echo ${username-”lferrari”}
lferrari
$ username=luca
$ echo ${username-”lferrari”}
luca
 ${parameter=default}, se “parameter” non è istanziato, gli
attribuisce il valore “default”.
 $ echo ${username=”lferrari”}
$ echo $username
lferrari
 ${parameter+value}, se “parameter” è istanziato, usa value,
altrimenti usa la stringa vuota.
 $ echo ${username+andrea}
andrea
$ echo ${pippo+andrea}
1.64
# null string
Sostituzione dei parametri
 ${parameter?msg}, Se “parameter” è istanziato, lo
usa, altrimenti stampa msg.
 $ value=${total?’total is not set’}
-bash: total: total is not set
$ total=10
$ value=${total?’total is not set’}
$ echo $value
10
Esempio:
#!/bin/bash
OUTFILE=symlinks.list
directory=${1-`pwd`}
for file in “$( find $directory -name “*.sh” )”
do
echo “$file”
done | sort >> “$HOME/$OUTFILE”
1.65
Operazioni avanzate sulle stringhe
 ${string#substring},
elimina
la
più
corta
occorrenza corrispondente a substring dall’inizio di string.
 $ pippo=abbcaabccbcabcdbcdaba
$ echo ${pippo#a*c}
aabccbcabcdbcdaba
$ echo ${pippo##a*c} # strips the longest match
daba
1.66
Operazioni avanzate sulle stringhe
 ${string/substring/replacement}, elimina la prima
occorrenza
corrispondente
a
rimpiazzandola con replacement.
substring
in
string,
 $ pippo=abbcaabccbcabcdbcdabab
$ echo ${pippo/ca/11}
abb11abccbcabcdbcdabab
$ echo ${pippo//ca/11}
abb11abccb11bcdbcdabab
# replaces all matches
$ echo ${pippo/[ab]?c/000}
a000aabccbcabcdbcdabab
$ echo ${pippo/c*a/\!}
abb!b
$ echo ${pippo//b?/00}
a00caa00c00a00d00da00b
1.67
Funzioni
 Le funzioni rendono gli scripts più facili da mantenere.
Sostanzialmente, permettono di suddividere il programma
in pezzetti più piccoli. Una funzione esegue un’azione
definita dall’utente, e può rendere un valore se l’utente lo
desidera.
 #!/bin/bash
hello()
{
in
function
echo
“You
are
hello()”
}
echo “Calling function hello()…”
hello
echo “You are now out of function hello()”
 Sopra, abbiamo richiamato la funzione hello() col suo
nome usando la linea: hello. Quando questa linea viene
eseguita, bash cerca nello script la linea hello(). La
trova, e ne esegue i contenuti.
1.68
Funzioni
#!/bin/bash
check() {
if [ -e "/home/$1" ]
then
return 0
else
return 1
fi
}
echo “Enter the name of the file: ” ; read x
if check $x
then
echo “$x exists !”
else
echo “$x does not exist !”
fi.
1.69
Massimo comun divisore
#!/bin/bash
# gcd.sh: massimo comun divisore
# Si usa l’algoritmo di Euclide
# Il “massimo comun divisore" (gcd) di due interi è il più grande intero
# che divide entrambi, senza che ci sia del resto.
# L’algoritmo di Euclide utilizza divisioni successive.
# Ad ogni passo, dividend <--- divisor, divisor <--- remainder
# finchè remainder = 0. Nell’ultimo passo, si ha gcd = dividend.
ARGS=2 E_BADARGS=65
if [ $# -ne "$ARGS" ]
then
echo "Usage: `basename $0` first-number second-number"
exit $E_BADARGS
fi
1.70
Massimo comun divisore
gcd ()
{ dividend=$1
divisor=$2
remainder=1
until [ "$remainder" -eq 0 ] do
let "remainder = $dividend % $divisor"
dividend=$divisor
divisor=$remainder
done }
# Last $dividend is the gcd.
gcd $1 $2
echo; echo "GCD of $1 and $2 = $dividend“
1.71
Script 1: estrarre una carta a caso da un mazzo
#!/bin/bash
Suites=“Clubs Diamonds Hearts Spades”
Denominations=“2 3 4 5 6 7 8 9 10 Jack Queen King Ace”
# Read into array variable.
suite=($Suites)
denomination=($Denominations)
# Count how many elements.
num_suites=${#suite[*]}
num_denominations=${#denomination[*]}
# Extract
echo -n "${denomination[$((RANDOM%num_denominations))]}
of "
echo ${suite[$((RANDOM%num_suites))]}
1.72
Script 2: cambiare i nomi di tutti i file mettendoli in
lettere minuscole
#!/bin/bash
for filename in *
# Traverse all files in directory.
do
fname=`basename $filename`
# Change name to lowercase.
n=`echo $fname | tr A-Z a-z`
if [ “$fname” != “$n” ]
#
Rename
only
files
not
lowercase.
then
mv $fname $n
fi
1.73
done
already
Script 3: Confrontare due files con uno script
#!/bin/bash
ARGS=2 # Two args to script expected.
if [ $# -ne “$ARGS” ]; then
echo “Usage: `basename $0` file1 file2” ; exit 1
fi
if [ ! -r "$1“ ] || [ ! -r "$2" ] ; then
echo “Both files must exist and be readable.” ; exit
2
fi
cmp $1 $2 &> /dev/null
# /dev/null buries the output of the “cmp” command.
# Also works with 'diff', i.e., diff $1 $2 &> /dev/null
if [ $? -eq 0 ] # Test exit status of “cmp” command.
then
echo “File \“$1\” is identical to file \“$2\”.”
else
echo “File \“$1\“ differs from file \“$2\”.”
fi
1.74
Esercizio
#!/bin/bash
MAX=10000
for((nr=1; nr<$MAX; nr++)) do
let "t1 = nr % 5"
if [ "$t1" -ne 3 ]
then continue fi
let "t2 = nr % 7"
if [ "$t2" -ne 4 ]
then continue fi
let "t3 = nr % 9"
if [ "$t3" -ne 5 ]
then continue fi
break # What happens when you comment out this line? Why?
done
echo "Number = $nr"
exit 0
1.75
Scarica

La shell bash - Dipartimento di Ingegneria dell`Informazione