La shell bash
La shell e' un programma che interpreta i comandi dell'utente. I
comandi possono essere dati da terminale, oppure contenuti in file
testo
(detto
script), che viene letto ed eseguito dalla shell. Una shell
puo' essere:
La shell di login viene attivata automaticamente all'atto di login.
Interpreta prima di tutto uno script uguale per tutti gli utenti e
scritto
dal sistemista: /etc/profile. Successivamente esegue uno script
definito dall'utente, nella propria home directory. Il nome di questo
script
varia a seconda del tipo di shell.
Per la bash, esegue solo il primo script fra:
~/.bash_profile, ~/.bash_login, ~/.profile
con l'operazione source (spiegata dopo).
L'utente puo' quindi "personalizzare" il suo ambiente di lavoro
usando
lo script di login. Inoltre, anche alla sua attivazione la shell esegue
automaticamente lo script ~/.bashrc , sempre con operazione
source.
All'uscita della sessione viene eseguito lo script ~/.bash_logout.
Se uno o piu' di questi script non esistono, non viene dato nessun
errore.
~ indica la home directory, e puo' anche
essere
scritto come $HOME.
Uno script e' dunque una lista di comandi Unix o di shell. Se non
specificato
altrimenti si suppone che sia la shell sh (che in Linux e'
identificata
con bash) ad interpretarli. Se si vuole utilizzare un altra
shell,
si indica sulla prima linea. Per vedere alcune caratteristiche delle
diverse
shell, leggere shell.txt.
#! /bin/tcsh
......
il carattere "#" in prima colonna, senza "!" dopo indica una riga di
commento.
Negli script non si eseguono solo comandi, si possono anche definire
delle variabili d'ambiente che possono poi essere riferite nei
comandi,
premettendo il carattere "$".
Ci sono almeno due modi per eseguire uno script:
- dandone semplicemente il nome, con eventuali argomenti. In questo
caso
viene attivata una nuova shell (sh, tcsh, ksh o altro, come indicato
nella
prima linea dello script) che esegue i comandi dello script e poi
termina
(vedi lo schema di esecuzione
comandi della sezione Architettura di Unix). Lo script deve essere
marcato "eseguibile" attraverso il comando chmod .
- attraverso il comando source (abbreviato anche con il
carattere
".") seguito dal nome dello script. In questo caso i comandi sono
eseguiti
all'interno della shell corrente.
Parlando di comandi ci sono diverse possibilita':
- Comandi interni o di shell. Sono comandi eseguiti internamente
alla
shell,
come cd, pwd.
- Comandi esterni. Sono programmi, ad esempio comandi Unix, esterni.
- Alias. E' possibile rinominare i comandi, cambiando anche il modo
di
passare
parametri.
- Funzioni. Negli script sono definibili delle funzioni. Non ce ne
occupiamo
in questi appunti, si rinvia al manuale.
Cenni di Grammatica della shell bash
Un pipeline e' un comando o una sequenza di comandi separati
dal
carattere "|". In questo caso l'output di un comando e' preso
come
input dal successivo. Vedere esempio in pipe
di comandi .
Una lista e' una sequenza di pipeline separati da un
operatore
fra
- ; i comandi sono
eseguiti
in
sequenza
- & i comandi che precedono
questo carattere
vengono messi in esecuzione in background
- && operatore booleano and:
se
il valore in output e' vero, si va avanti nell'esecuzione, altrimenti
si
termina la lista
- ||
operatore
booleano or:
se l'output del comando a sinistra e' vero, non si prosegue
e terminata da uno fra ; &
<newline>.
Sia i pipeline che le liste restituiscono un valore di ritorno, =0
oppure
=1 in dipendenza dell'operazione effettuata.
E' possibile definire dei compound command come: (i
parametri
fra [ ] sono opzionali)
- for name [ in word; ] do list; done
dove name e word sono stringhe di caratteri. word e' scritto
usando una grammatica regolare (di cui si e' gia' parlato a proposito
di
identificatori di file )
e genera un insieme di elementi (stringhe di caratteri). name
(una
variabile) assume uno alla volta ognono dei valori di word, e ogni
volta
e' eseguito list.
- for
(( expr1 ; expr2 ; expr3 )) ; do list ; done
corrisponde al for del C, in senso aritmetico. Viene valutato il
valore expr1, quindie' valutato ripetutamente il valore expr2. La
lista di comandi e' eseguita, ogni volta che expr2 e' diverso da 0. Se
expr3 e' omesso, e' inteso =1
# script sh3.sh
# esempio di for aritmetico
for (( i=0; i<4 ; i++))
do echo "valore i $i"
done
ESECUZIONE
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ sh3.sh
valore i 0
valore i 1
valore i 2
valore i 3
- case word in [pattern [ | pattern ] [....] list ;; ] ... esac
word e' espanso, ottenedo una stringa. Se fa il match con uno degli
elementi indicati come pattern, viene eseguita la lista collegata, poi
termina il comando. Se no, esce con status=0.
- if list then list [ elif list the list ] ... [ else list ] fi
Si esegue "if list ", se lo status di ritorno e' =0 allora viene
eseguito
"then list ". Altrimenti si esegue "else list ", oppure gli altri "if "
nested.
- while list do list done
until list do list done
Il primo comando esegue "do list " finche' l'ultimo comando di "list
" ritorna status=0. Il secondo fa lo stesso, ma con il test negato.
- ( list )
{ list; }
gruppo di comandi eseguiti in una subshell o nella shell corrente.
Attenzione ai blank, eliminarli puo' causare errore.
E' possibile assegnare delle variabili di tipo stringa di
caratteri,
poi riferibili nella stessa shell. Una variabile e' assegnata con lo
statement
name=[ value ]
Se value e' assente il valore e' la stringa nulla. L'assegnazione e'
valida solo nella shell in cui e' eseguita, ma puo' essere esportata
nelle
shell di livello inferiore con il comando di shell export.
Il comando export da solo lista le variabili esportate, con
i valori loro assegnati.
Non mettere blank prima o dopo il carattere =.
ESEMPIO
export A=100
B=1000
export B
Il nome e il valore di alcune variabili sono predefinite dalla shell, e
non possono essere modificate direttamente, altre sono definite come
nome,
ma non assegnate (sono utilizzate implicitamente da alcuni comandi):
- Predefinite ed assegante
PPID parent del processo
PWD current working directory
UID user ID
EUID effective user ID
........ - Predefinite, non assegnate
PATH pathname fra cui cercare i comandi.
HOME home directory del current user
PS1 primary prompt string
.......
Il comando cd, ad esempio, e' interpretato dalla shell, e
riassegna
la variabile PWD, utilizzata dal comando pwd.
Quando si da' un comando (o si richiede l'esecuzione di un processo)
la shell ricerca il file eseguibile nelle directory indicate nella
variabile
PATH. Si indicano quindi in sequenza i path assoluti delle directory,
separate
dal carattere :. Il carattere "." indica la WD generica, cioe'
quella
in cui ci si trova al momento di dare un comando, non la WD in cui e'
eseguito
lo script "star". Si consiglia di metterla sempre per ultima.
Alias
Si possono definire nomi diversi per i comandi.
Esempio
alias del='rm -i'
alias dir='ls -l'
Per l'uso del carattere ', vedere piu' avanti quoting.
Per conoscere la lista degli alias si usa il comando alias senza
argomenti.
alias nome da' l'alias definito per quel nome.
Il comando set stampa le variabili che sono state definite.
Il valore di una variabile si referenzia facendola precedere dal
carattere
$.
Esempio
#! /bin/bash
# script star
echo HOME=$HOME
cp $HOME/main.c tmp || echo "no main.c"; exit
echo "ho eseguito"
cp $HOME/include/main.h /tmp || echo "no main.h" | exit
cd /tmp; tar cvf main.tar main.c main.h
mv main.tar $HOME
rm -f main.*
cd $HOME
# fine
ESECUZIONE
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ star
HOME=/home/vittoria
cp: cannot stat `/home/vittoria/main.c': No such file or directory
no main.c
Nell'esempio sopra si aggiunge (in coda) la directory corrente (i due
punti
servono da separazione) e la directory $HOME/bin, come directory in cui
effettuare la ricerca degli eseguibili. Viene poi stampato il valore di
PATH. Si copiano i file main.c e main.h nella directory "./tmp". Se i
file
non sono presenti nelle directory indicate, lo script termina (exit).
Si
passa nella sottodirectory "/tmp". Si compone il file tar "main.tar".
Si
cancellano i file copiati nella WD e si torna nella home directory.
Esempio
#! /bin/bash
# script sh.sh
# eseguire senza "source".
# la variabile B e' visibile nella nuova shell
# non lo e' invece dopo l'exit.
# la variabili A1 non e' mai visibile.
A1=100
echo script sexp A1 = $A1
export B=200
echo script sexp B=$B
/bin/bash #attiva una nuova shell, da cui si puo' uscire
con exit
# fine sh.sh
esecuzione normale
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ sh.sh
script sexp A1 = 100
script sexp B=200
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ echo $A
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ echo $B
200
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ exit
exit
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$
esecuzione
come "source"
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ . sh.sh
script sexp A1 = 100
script sexp B=200
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ exit
exit
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$ echo $A1
100
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell$
Comandi builtin
Questa e' solo una lista limitata e alcuni comandi sono gia' stati
visti
prima o nelle sezioni precedenti. Le parentesi quadre indicano
parametri
opzionali, tranne nel caso del comando test abbreviato.
source nome_script argomenti
alias [ name [ = value ] ... ]
bg [ jobspec ]
fg [ jobspec ]
jobs [ -lnp ] [ jobspec .... ]
cd [ dir ]
kill [ -s sigspec | -sigspec ] [ pid | jobspec ] ...
echo [ -neE ] [ arg ... ]
Output argomenti separati da blank. Ritorna 0.
exec [ [ - ] command [ arguments ] ]
Esegue il comando nella shell corrente.
Senza command puo' avere una redirezione dell'I/O.
exit [ n ]
Exit dallashell con status = n.
export [ -nf ] [ name [ = word ] ] ....
help [ pattern ]
pwd
read [ -r ] [ name ... ]
Legge una linea dallo standard input: la prima word e' assegnata al
primo
name e cosi' via.
set [ ... ] [ arg ... ]
test expr oppure
[ expr ]
Le parentesi quadre sono in questo caso obbligatorie, come pure il
blank
di separazione.
Ritorna 0 o 1 in dipendenza della valutazione di expr.
umask [ -s ] [ mode ]
unalias [ name ... ]
Rimuove name dalla lista degli alias definiti.
unset [ -fv ] [ name ... ]
Rimuove la variabile name dall'insieme delle variabili definite.
wait [ n ]
Aspetta la terminazione del processo o job indicato.
Il comando test e' molto usato, specialmente nella sua forma
abbreviata [ expr ]. Si possono ad esempio verificare
proprieta'
dei file, o uguaglianza di stringhe (non valori numerici).
Alcuni esempi:
test -d file vero se
file esiste ed e' una directory.
test -f file vero se
il file esiste ed e' un file di dati.
test -e file vero se
il file esiste.
test -L file vero se il file
esiste es e' link simbolico.
test -r file vero se
il file esiste ed e' leggibile.
test -x file vero se
il file esiste ed e' eseguibile.
test stringa1 = stringa2 vero se
uguali.
test stringa1 != stringa2 vero se
diversi.
Il comando test expr puo' anche
essere
scritto come: [ expre ]
ESEMPIO
#! /bin/bash
# script di login
# attenzione ai blank nella prossima linea
if [ "$LOGNAME" = "root" ]; then
echo " Welcome dear $LOGNAME"
if [ -f $HOME/hello ];
then echo $HOME/hello
fi
fi
Esempio
#! /bin/sh
# script sbh
if [ "$SHELL" = "/bin/bash" ]; then
PS1="sono la shell bash > "
fi
# fine
Eseguire lo script sbh come "source sbh". Attenzione ai blank
obbligatori.
Parametri posizionali
Sono i parametri della chiamata ad uno script.
Si identificano con il carattere $ seguito dall'indice della posizione.
Se si usano numeri di 2 cifre, si racchiudono fra parentesi graffe.
Esempio
nome_script alfa 10
$0 $1 $2
Parametri speciali:
$0 nome dello script.
$@ collezione di tutti i parametri (a partire da $1).
Quando
si usa all'interno dei doppi apici (vedi quoting
piu' avanti) rappresenta l'insieme dei parametri come word separate:
"$1"
"$2" ... "$n".
$* stringa ottenuta concatenando tutti i parametri in
un'unica stringa. I parametri sono separati fra loro dal primo
carattere
della variabile IFS (normalmente lo
spazio). Quindi "$*" indica "$1 $2 $3 .. $n".
$# numero di parametri presenti.
$$ processs id della shell.
$- flag opzionali.
Esempio
#! /bin/sh
# parte di /usr/src/tool/mkboot di MINIX
case "$#:$1" in
1:bootable | 1:install | 1:hdtest | [12]:fdboot)
action=$1 dev=$2
;;
*) echo "Usage: $0 [bootable | hdinstall | hdtest | fdboot [device]]" >&2
exit1
esac
. /etc/fstab
# ... eccetera
Prompting
Il primary prompt PS1 puo' essere modificato run time.
Ci sono per questo dei caratteri speciali:
\t tempo corrente
\d data corrente
\w current working directory
\u username
\h hostname
\# command number
\! history number
ecc.
ESEMPIO
PS1='[\!]\h:\u > '
Espansioni
Ci sono diversi tipi di espansioni che vengono effettuate dalla shell
prima
di attivare i comandi richiesti:
- espansione delle graffe: { elenco di word separate da
virgola }. Esempio: p{a,o,i}logenera
le tre word palo, polo, pilo.
- espansione della tilde. ~gianuzzi indica la home
page dell'utente
gianuzzi.
- espansione di parametri e variabili. Gia' visto
precedentemente.
- sostituzione dei comandi. `commad`:
bash
esegue
command e rimpiazza il tutto con il suo output. Attenzione alla
direzione
del carattere apice singolo, diverso dal carattere '. bash
esegue
command e rimpiazza il tutto con il suo output. Vedere script s1 in un
esempio successivo.
- espansione espressioni aritmetiche. Vengono calcolate.
- suddivisione in word. Ogni word ottenuta applicando le
espansioni
o sostituzioni viste sopra, sono separate fra di loro usando la
variabile
d'ambiente IFS, che normalmente contiene
i caratteri spazio tab newline.
- espansione del path name. Si espandono i caratteri
*, ? [
- * e' un pattern che fa il match con qualunque stringa,
anche nulla.Vi
e' una sola eccezione: i file che iniziano con il carattere "."
ESEMPIO: i comandi "ls *" e "ls .*" sono diversi. - ? indica
un carattere singolo carattere non nullo.
- [ ] fa il match con uno qualunque dei caratteri
inclusi
fra le parentesi.
Ad esempio [a-z1]* indica qualunque stringa che inizia con un carattere
alfabetico da "a" a "z", minuscoli, oppure con il carattere 1, ed e'
seguito
da un qualunque numero, anche zero, di caratteri qualunque. L'ordine
dei
caratteri e' quello della codifica ASCII.
ESEMPIO
echo /usr/src/*/core
ls /usr/src/kernel/*.c
Altri metacaratteri sono & | && || ecc. he
esprimono
operazioni logiche o di concatenazione di stringhe. Qualunque carattere
preceduto da "\" perde il suo (eventuale) valore di metacarattere.
ESEMPIO
rm `find . -name "*.obj"`
Quoting
Ci sono altri tipi di apici oltre al ` usato per il
command
substitution:
- '...' tutto quanto e' racchiuso fra gli apici
e'
preso
come carattere semplice (escluso il carattere apice stesso). Non e'
quindi
fatta nessuna espansione.
- "..." come con il carattere precedente, ma con
delle eccezioni.
Viene fatta l'espansione dei parametri (interpretazione del carattere
$),
viene fatta la command substitution, si considera il carattere "\".
Esempio
#! /bin/bash
echo "numero argomenti = $#"
for i in $*
do
echo "argomento $i"
done
Esempio
#! /bin/bash
# script s1
VAR=`ls` # lista corta di tutti
echo VAR=$VAR # i file della WD
for i in $VAR; do
ls -ld $i; done # lista lunga
Funzione Set
set [opzioni] [argument-list]
Senza opzioni mostra solo il nome e il valore delle variabili di
shell. Inoltre setta gli argomenti posizionali ($1, $2 ecc.) con le
word
in argument-list. Vediamo alcuni esempi.
Esempio 1
Il comando date restituisce la data e l'ora attuale, ed e'
costituito
da 6 campi (giorno settimana, mese, giorno del mese, ora, tipo di ora e
anno).
Eseguendo lo script
#!/bin/bash
date # esegue il comando date
set $(date) # assegna gli argomenti posizionali
echo "$@"
echo "$2 $3, $6"
si ottiene ad esempio:
Mon Apr 14 12:47:06 CEST 2008
Mon Apr 14 12:47:06 CEST 2008
Apr 14, 2008
Nota: la notazione $(date) e' una nuova (piu' comado) sintassi per il
command
substitution.
Provare ad eseguire la shell:
#!/bin/bash
command=pwd
echo "valore di command: $command."
command=$(pwd)
echo "valore di command: $command."
Esecuzione
vittoria@krypton:~/didatt/so2/esempiSO2/ScriptShell/bashSO2$ prova.sh
valore di command: pwd.
valore di command: /home/vittoria/didatt/so2/esempiSO2/ScriptShell/bashSO2.
Esempio 2
#!/bin/bash
if [ X"$@" = X ]; then
echo "vuole come argomento il nome di un file"; exit
fi
filename="$1"
set $(ls -il $filename)
inode="$1"
size="$6"
echo
( printf "Name Inode Size \n" ; ls -l | echo "$filename $inode $size") | column -t
Provare ad eseguire dando come parametro il nome di un file della
working
directory.