Date post: | 17-Feb-2019 |
Category: |
Documents |
Upload: | duongkhuong |
View: | 230 times |
Download: | 1 times |
La shell di UnixUso interattivo e scripting
Paolo Baldan
Dipartimento di Informatica
Universita di Venezia
La shell di Unix – p.1/196
Cos’è una shell
Shell significa conchiglia: racchiude il kernel ed è la superficie concui l’utente entra in contatto quando vuole interagire con il sistema.
Fondamentalmente una shell è un interprete di comandi, che fornisceall’utente un’interfaccia verso un ricco insieme di utility e unlinguaggio di programmazione per “combinare” queste utility.
Si distinguono due tipi di funzionamento:
interattivo: i comandi sono digitati da tastiera dall’utente
non interattivo: i comandi sono contenuti in file, detti script, cheuna volta definiti possono essere utilizzati in modo (quasi)indistingubile rispetto ai comandi ordinari (es. quelli in /bin/) epermettendo così di personalizzare l’ambiente di lavoro.
La shell di Unix – p.3/196
Alcune caratteristiche della shell
Permette un controllo dell’input/output dei comandi (tramite icostrutti di ridirezione) e del loro ambiente di esecuzione.
fornisce un piccolo insieme di comandi, detti builtin cheimplementano funzionalità altrimenti difficili da realizzare (es.history, getopts, kill, e pwd) o addirittura impossibili (es. cd,break, continue, e exec).
fornisce un linguaggio di programmazione, con variabili, funzioni ecostrutti per il controllo del flusso.
Nel tempo, le shell sono state aricchite con caratteristiche orientatepiù verso l’uso interattivo che verso la programmazione. Alcune ditali caratteristiche sono il controllo dei job, l’editor da linea dicomando, history ed alias.
La shell di Unix – p.4/196
Operazioni della shell
1. Legge l’input (dalla tastiera del terminale utente, da una stringapassata come argomento (opzione -c) o da un file di script).
2. Scompone l’input in parole ed operatori, rispettando le regole diquoting. In questo passo viene anche effettuata l’espansione deglialias.
3. Esegue vari tipi di espansioni (es. espansione di percorso oespansione della storia).
4. Esegue ogni redirezione necessaria e rimuove gli operatori diredirezione ed i loro operandi dalla lista degli argomenti.
5. Esegue il comando.
6. Aspetta che l’esecuzione termini e quindi ne rileva lo stato.
La shell di Unix – p.5/196
Tipi di shell
Esistono un certo numero di shell, tra cui . . .- Bourne shell (sh)- Korn shell (ksh)- C shell (csh ed il suo successore tcsh)- Bourne again shell (bash)
Le varie shell presentano numerosi aspetti comuni. Differiscono perla sintassi e per alcune caratteristiche e funzionalità più sostanziali.
Ci concentreremo su bash, la shell (quasi) standard in ambienteLinux. Il nome è un acronimo di Bourne-Again SHell, unriconoscimento a Steve Bourne, autore di un diretto antenato dellashell UNIX originaria /bin/sh.
Bash è una shell sh-compatibile, che ne migliora aspetti interattivi eprogrammativi.
La shell di Unix – p.6/196
Bash
Normale programma eseguibile bash (tipic. nella directory /bin/).
bash [<opzioni>] [<file-script>] [<argomenti>]
Non interattiva: se <file-script> è presente bash tenta dieseguirlo come uno script.
Interattiva: La shell interagisce con l’utente, mostra un prompt,ovvero “invito” a inserire dei comandi (modalità forzata conl’opzione -i).
Shell interattiva di login
Shell interattiva normale
La distinzione influenza la scelta dei file di configurazione letti.
La shell di Unix – p.7/196
Shell interattiva di login
Una shell di login è quella che si ha di fronte quando è statacompletata la procedura di login (può essere esplicitamente avviatacon l’opzione -login).
se non è stata specificata l’opzione -noprofile, tenta di leggere edeseguire il contenuto dei file di configurazione:
/etc/profile
˜/.bash profile oppure ˜/.bash login oppure ˜/.profile(in questo ordine).
Al termine della sessione di lavoro, se il file esiste, legge ed esegue ilcontenuto di ˜/.bash logout.
La shell di Unix – p.8/196
Shell interattiva di login
I file di configurazione letti da una shell di login vengono solitamenteutilizzati per
stabilire il path (le directory in cui cercare i comandi)
definire funzioni
definire il valore delle variabili di interesse (di ambiente e non);
stabilire il tipo e i parametri del terminale
stabilire i permessi di default sui file (con umask)
La shell di Unix – p.9/196
Shell interattiva normale
Se non viene specificata una delle opzioni -norc o -rcfile), legge edesegue (se esiste) il file ˜/.bashrc.
Collegamenti tra file di configurazione: spesso è opportuno leggere˜/.bashrc anche per shell di login. A questo fine all’interno del file˜/.bash profile si inserisce...
if [ -f ˜/.bashrc ];
then
source ˜/.bashrc
fi
...
Il significato è semplice: viene controllata l’esistenza del file ˜/.bashrc e,se presente, viene caricato ed eseguito.
La shell di Unix – p.10/196
Shell non interattiva
Esegue un file di script
bash [<opzioni>] script arg1 ...argn
Parametri posizionali: $1, $2, $3, ecc. usati nello script per indicaregli argomenti. Il parametro $0 indica il nome dello script.Esempio:
echo "Script $0"
echo "Primo parametro $1"
echo "Secondo parametro $2"
La shell esegue i comandi dello script e quindi termina l’esecuzione.
La shell di Unix – p.11/196
Shell non interattiva / B
Se dopo le opzioni ci sono altri argomenti, il primo di questi vieneinterpretato come il nome di uno script di shell (a meno che nonsiano state usate le opzioni ...)
-c <stringa>: vengono eseguiti i comandi contenuti nellastringa. Gli argomenti successivi sono associati ai parametriposizionali a partire da $0.
-s: la shell legge i comandi dallo standard input.
All’avvio in modalità non interattiva, Bash esegue il file nellavariabile di ambiente BASH ENV (se non vuota).
In pratica, BASH ENV indica un file di configurazione da eseguireprima dello script. Tipicamente BASH ENV è vuota, oppure non esisteaffatto.
La shell di Unix – p.12/196
Conclusione
La conclusione del funzionamento della shell, quando si trova inmodalità interattiva, si ottiene normalmente attraverso il comandointerno exit, oppure eventualmente con il comando interno logout
se si tratta di una shell di login.
Se invece si tratta di una shell avviata per interpretare uno script,questa termina automaticamente alla conclusione dello script stesso.
La shell di Unix – p.13/196
Alias
La shell Bash attraverso i comandi interni alias e unalias offre lapossibilità di definire ed eliminare alias, ovvero nomi cherappresentano comandi, possibilmente composti e con opzioni
alias <nome>=<comando>
unalias <nome>
prima di eseguire un comando di qualunque tipo, la shell cerca laprima parola del comando all’interno dell’elenco degli alias; se latrova, la sostituisce con il suo alias.
Attenzione: prima e dopo del segno “=” non compaiono spazi.
il nome di un alias non può contenere il simbolo =.
La shell di Unix – p.15/196
Alias / B
Possono essere utilizzati per
associare un nome mnemonico ad un comandoalias cerca=grep
alias trovatex=’find . -name *.tex’
correggere errori di sintassi comunialias emcas=emacs
come shortcut per riferire comandi molto lunghialias cdtex=‘cd /home/rossi/projects/LaTeX/sources‘
possono contenere caratteri specialialias printall=‘lpr *.ps‘
La shell di Unix – p.16/196
Alias / C
Si può effettuare l’alias di un alias: Bash, quando espande un aliasconsidera la prima parola del testo di rimpiazzo e verifica se sonopossibili ulteriori espansioni.
alias printall=‘lpr *.ps‘
alias pa=printall
Per evitare cicli infiniti, se la prima parola del testo di rimpiazzocoincide con un alias già espanso, non si prosegue ulteriormentenell’espansione. Ad esempio è legale:
alias ls=‘ls -F‘
Ridefinire comandi esistenti, aggiungendo opzioni desiderate comedefault, è uno degli usi più comuni degli alias:
alias rm=‘rm -i‘
La shell di Unix – p.17/196
Alias / D
Nota: Se l’ultimo carattere del testo di rimpiazzo dell’alias è unospazio o una tabulazione, allora anche la parola successiva vienecontrollata per una possibile sostituzione attraverso alias.
Utile per l’aliasing delle directory. Es.
/home/rossi$ alias mydir=/home/rossi/projects/LaTeX/
/home/rossi$ cd mydir
bash: cd: mydir: File o directory inesistente
/home/rossi$ alias cd=‘cd ‘
/home/rossi$ cd mydir
/home/rossi/projects/LaTeX$
Gli alias non vengono espansi quando la shell funziona in modalitànon interattiva; di conseguenza, non sono disponibili durantel’esecuzione di uno script.
La shell di Unix – p.18/196
Alias / E
A differenza della shell C, Bash non consente di definire alias conargomenti. Se necessario, bisogna utilizzare le funzioni (chevedremo nel seguito). In generale, l’utilizzo di alias è superatodall’uso delle funzioni.
Come accade per altri meccanismi di personalizzazione della shell,gli alias sono tipicamente definiti nei file di configurazione(.bashrc).
Per conoscere la lista degli alias attualmente validi Bash fornisce ilcomando alias:
/home/rossi$ alias
alias c=’clear’
alias cd=’cd ’
alias cp=’cp -i’
... La shell di Unix – p.19/196
Opzioni
Le opzioni sono una sorta di “switch”, con valore booleano, cheinfluenzano il comportamento della shell.
set +o <option> disattiva l’opzioneset -o <option> attiva l’opzione
Attenzione: l’uso di + e - è controintuitivo.
Quasi ogni opzione ha una forma breve. Ad es.
set -o noglob
equivale a
set -f
la lista delle opzioni e dei relativi valori si ottiene con
set -oLa shell di Unix – p.20/196
Opzioni / A
Alcune opzioni comuni ed il relativo significato
Opzione Significato
emacs emacs editing mode
ignoreeof non permette il logout con Ctrl-D
noglob non espande i metacaratteri, come * e ?
nounset dà errore se si usa una variabile non def.
La shell di Unix – p.21/196
Variabili di shell
Alcuni aspetti rilevanti dell’ambiente di esecuzione della shell non sonocaratterizzabili semplicemente con valori on/off come le opzioni. Aspettidi questo tipo sono specificati tramite variabili
una variabile è un nome al quale è associato un valore
il nome è una stringa alfanumerica (contiene lettere, cifre eunderscore, ma il primo carattere non può essere una cifra)
il valore è una stringa di caratteri (vedremo che Bash supportaanche altri tipi ...)
Per assegnare un valore ad una variabile
<varname>= [<value>]
Se la variabile <varname> non esiste allora viene creata, altrimenti ilsuo valore precedente viene sovrascritto.
La shell di Unix – p.22/196
Variabili di shell / B
Una variabile si dice definita quando contiene un valore,possibilmente la stringa vuota. Può essere cancellata con
unset <varname>
Per riferire il valore di una variabile si utilizza la notazione
$<varname> oppure ${<varname>}
Alcuni esempi
Y = terra assegna valore ‘terra‘ alla variabile Y
echo $Y stampa il valore di Y
X = $Y assegna il valore di Y a X
X = Y assegna il valore ’Y’ a X
Z = assegna la stringa vuota a Z
unset Y cancella Y
La shell di Unix – p.23/196
Variabili di shell / C
Alcuni operatori permettono di verificare se una variabile esiste e operaredi conseguenza
${<varname>:-<val>}
Se <varname> esiste ed ha valore non vuoto, ritorna il suo valore,altrimenti ritorna <val>
${<varname>:=<val>}
Se <varname> esiste ed ha valore non vuoto, ritorna il suo valore,altrimenti assegna <val> a <varname> e ritorna <val>.
${<varname>:?<message>}
Se <varname> esiste ed ha valore non vuoto, ritorna il suo valore,altrimenti stampa il nome della variabile seguito dal messaggio<message>.
La shell di Unix – p.24/196
Variabili di shell - builtinAlcune variabili sono assegnate automaticamente da Bash stessa. Tipicamente hannonome costituito da lettere maiuscole. Ecco alcuni esempi:
SHELL (csh) - Il pathname completo della shell di login.RANDOM (ksh) - Un numero intero casuale.SECONDS (ksh) - Il numero di secondi trascorsi dall’avvio della shell.PWD (ksh) - La directory corrente.OLDPWD (ksh) - La directory corrente visitata precedentemente.HISTCMD (bash) - L’indice nel registro storico dei comandi.UID (bash) - Lo UID dell’utente.GROUPS (bash) - Un array contenente i numeri GID di cui l’utente è membro.HOME - La propria home directoryHOSTTYPE (bash) - Il nome del tipo di elaboratore.OSTYPE (bash) - Il nome del sistema operativo.MACHTYPE (bash) - Architettura e sistema operativo utilizzato.BASH_VERSION (bash) - Il numero di versione di Bash.BASH (bash) - Il percorso completo della copia corrente dell’eseguibile bash.SHLVL (bash) - Il livello di annidamento dell’eseguibile bash.
La shell di Unix – p.25/196
Variabili di shell - builtin / A
Editing mode: Alcune variabili sono legate alle funzionalità di editingdella riga di comando.
HISTFILE il file dove salvare la storia (default ˜/.bash_history)HISTSIZE massimo numero di comandi nella storia
Variabili di prompt: in particolare PS1 influenza il cosiddetto promptprimario, il prompt ordinario della shell interattiva.Alcune stringhe hanno un significato particolare\u nome dell’utente
\s nome della shell
\v versione della shell
\w la working directory
\h hostname
Es. con PS1=\u@\h:\w\$, si otterrà un prompt del tipo:rossi@ihoh:/home/rossi$
La shell di Unix – p.26/196
Variabili di shell - builtin / B
Search path: Alcune variabili specificano i cammini nel file system che lashell deve seguire per cercare comandi e directory.
PATH: dove cercare il comando da eseguireLa variabile PATH è normalmente già definita ed ha un valore didefault. Tipicamente si vorrà solo aggiungere qualche cammino diricerca
PATH = $PATH":˜/bin/"
CDPATH: dove cercare la nuova working directory quando si esegueil comando cd <dirname>. Tipicamente conterrà le directory che siaccedono più spesso. Ad es.:
CDPATH=˜/:˜/projects
La shell di Unix – p.27/196
Variabili di ambiente
Le variabili di shell sono proprie dell’ambiente locale della shellstessa e quindi non sono, normalmente, visibili a programmi /sottoshell avviate dalla shell stessa.
Una classe speciale di variabili, dette variabili di ambiente sonoinvece visibili anche ai sottoprocessi.
Una qualsiasi variabile può essere resa una variabile di ambiente“esportandola”
export <varnames>
dove <varnames> è una lista di nomi di variabili separati da spazi.
Una variabile può essere definita ed esportata contemporaneamentecon la sintassi
export <varname> = <value>La shell di Unix – p.28/196
Variabili di ambiente / A
Alcune variabili builtin, come HOME, PATH e PWD sono variabili diambiente “per default”.
È anche possibile definire variabili nell’ambiente di un particolarecomando (sottoprocesso) facendo precedere il comando dalladefinizione delle variabili
<varnames> = <value> <command>
Digitando semplicemente export si ottiene una lista delle variabiliesportate dalla shell.
Si noti che le definizioni in .bashrc (file di ambiente) sono valide inogni sottoshell interattiva.
La shell di Unix – p.29/196
Script
Uno script è un programma di shell: file di testo che contienecomandi di shell (più in generale comandi per un interprete).
Per eseguire un file di script scriptname lo si fornisce comeargomento all’interprete
bash scriptname <args>
Alternativamente si può indicare l’interprete nella prima riga delloscript stesso:
se contiene solo un simbolo # lo script è intepretato dalla shellda cui è lanciato
se contiene #!pathname lo script è intepretato dalla shellidentificata da pathname. Es:
#!/bin/bash
La shell di Unix – p.31/196
Script / A
Nel secondo caso, se il file di script è eseguibile (chmod permette didare i diritti di esecuzione) e raggiungibile nei percorsi dellavariabile PATH può essere eseguito come un normale comando
scriptname <args>
Nota: Il programma che esegue uno script non è necessariamenteuna shell. Ad esempio, uno script awk può iniziare con
#!/bin/awk
Ma sono legittimi anche
#!/bin/cat
oppure
#!/bin/rm
La shell di Unix – p.32/196
Parametri posizionali
La shell prevede alcune variabili builtin, i parametri posizionali especiali, che risultano utili per lo scripting.
I parametri posizionali rappresentano gli argomenti dello script:
$n oppure ${n} valore dell’n-mo argomento
Se n è costituito da più di una cifra deve essere racchiuso tra graffe(es. ${10}).
I parametri sono assegnati dalla shell e possono essere solo letti.
Esempio (1)
#!/bin/bash
echo Ho un parametro che vale $1
echo ed un secondo che vale $2
exit 1
La shell di Unix – p.33/196
Parametri speciali (alcuni)
$0 Nome della shell o dello script.
$* Insieme di tutti i parametri posizionali a partire
dal primo. Tra apici doppi, rappresenta un’unica
parola composta dal contenuto dei parametri posizionali.
$@ Insieme di tutti i parametri posizionali a partire
dal primo. Tra apici doppi rappresenta una serie
di parole, ognuna composta dal contenuto del
rispettivo parametro posizionale.
Quindi "$@" equivale a "$1" "$2"... .
$# numero di parametri posizionali.
$$ PID della shell.
La shell di Unix – p.34/196
Uso dei parametri - esempi
Script che conta i propri argomenti (2):
#!/bin/bash
echo Sono lo script $0
echo Mi sono stati passati $# argomenti
echo Eccoli: $@
Esempio (anticipato)
#!/bin/bash
while true
do
if newer $1.dvi $1.ps; then
dvips $1 -o
fi
sleep 2s
doneLa shell di Unix – p.35/196
Funzioni
Bash offre la possibilità di definire funzioni, ovvero di associare unnome ad un programma di shell, che viene mantenuto in memoria epuò essere richiamato come un comando interno.
[function] <nome> () {
<lista-di-comandi>
}
Le funzioni sono eseguite nella shell corrente (e non in unasottoshell come gli script).
Parametri posizionali e speciali sono utilizzabili come negli script.
La shell di Unix – p.36/196
Funzioni - esempi
Per rimuovere l’indentazione di un file
noindent () {
sed -e ’s/ˆ *//’ $1 >${2:-$1.noindent};
}
Al posto degli alias, più potente
rmall () {
find . -name "$1" -exec rm -i {} \;
}
E molti altri ...
La shell di Unix – p.37/196
Espansione e quoting
Espansione: La shell di UNIX, prima di eseguire una linea dicomando, interpreta le variabili ed i simboli speciali, sostituendoli(espandendoli) con quanto “rappresentano”.
Quoting: Meccanismi di “quoting” permettono di inibirel’espansione e quindi di interpretare in modo “letterale” simboli chealtrimenti avrebbero un significato speciale.
Al termine del procedimento di espansione i simboli di quoting sonorimossi in modo che non ne resti traccia ad un eventuale programmache riceva questi dati in forma di argomenti.
La shell di Unix – p.39/196
Espansione
Bash prevede vari tipi di espansione della linea di comando, che vengonoeseguiti nel seguente ordine
1. espansione degli alias e della storia
2. espansione delle parentesi graffe (C shell);
3. espansione della tilde (C shell);
4. espansione delle variabili (Korn);
5. sostituzione dei comandi (Bourne e Korn);
6. espansione delle espressioni aritmetiche;
7. suddivisione in parole;
8. espansione di percorso o pathname.
La shell di Unix – p.40/196
Espansione degli alias e della storia
Se la prima parola della linea di comando è un alias, lo espande(ricorsivamente) secondo le regole descritte in precedenza.In particolare l’espansione si applica anche alla parola successivaquando il testo che sostituisce un alias termini con uno spazio.
Se un parola della linea di comando inizia con il carattere speciale“!” allora la shell interpreta la parola come un riferimento alla storiaEs.!n → n-ma riga di comando
!-n → riga di comando corrente -n
!! → riga di comando precedente
!string → riga di comando più recente che inizi per string
La shell di Unix – p.41/196
Espansione delle parentesi graffe
Meccanismo che permette la generazione di stringhe arbitrarieconformi ad un semplice pattern del tipo
<prefisso>{<elenco>}<suffisso>
dove <elenco> consiste di una lista di elementi separati da virgole.Il risultato è una serie di parole composte tutte dal prefisso e dalsuffisso indicati e all’interno uno degli elementi della lista.
Ad es.
c{er,as}care si espande in cercare cascare.
c{{er,as}c,ucin}are si espande in cercare cascare
cucinare.
La shell di Unix – p.42/196
Espansione delle parentesi graffe / A
Esempio:
/home/rossi$ mkdir agenda/{old,new}
Crea due sottodirectory old e new della directory agenda.
Esempio:
/home/rossi$ rm agenda/{old/file.{?,??},new/file.*}
rimuove i file nella directory agenda/old con nome file e suffissocostituito da uno o due caratteri, e i file nella directory agenda/new
con nome file e suffisso qualsiasi.
Nota: Le stringhe che risultano dall’espansione non sononecessariamente nomi di file esistenti (come accade per l’espansionedi percorso).
La shell di Unix – p.43/196
Espansione della tilde
Se una parola inizia con il simbolo tilde (˜) la shell interpreta quantosegue, fino alla prima barra obliqua (/), come uno username esostituisce questo prefisso con il nome della home directorydell’utente.
˜username → home directory di username
˜/ e ˜ si espandono nella home directory dell’utente attuale, ovveronel contenuto della variabile HOME.
˜/ , ˜ → propria home directory
Esempi:/home/rossi/report$ cd ˜
/home/rossi$
/home/rossi$ cd ˜bianchi
/home/bianchi$La shell di Unix – p.44/196
Espansione della tilde / A
Se la stringa che segue la tilde ˜ non è uno username, non avvienel’espansione.
Altre sostituzioni della tilde:
˜+ → working directory (contenuto di PWD)
˜- → working directory precedente (contenuto di OLDPWD)
Esempio:
/home/rossi/report$ cd ˜
/home/rossi$ cd ˜- (oppure cd -)
/home/rossi/report$
La shell di Unix – p.45/196
Espansione delle variabili
Se la shell trova nella linea di comando una parola che inizia per $
$stringa oppure ${stringa}
allora interpreta stringa come il nome di una variabile e la espandecon il suo contenuto.
Esempio
/home/rossi$ PARTE="Dani"
/home/rossi$ echo $PARTEele
/home/rossi$ echo ${PARTE}ele
Daniele
Esistono varie altre forme di sostituzione di variabili (forme ${...})che vedremo nel seguito (principalmente derivate dalla shell Korn).
La shell di Unix – p.46/196
Sostituzione dei comandi
Consente di espandere un comando con il suo (standard) output. Lasintassi, derivante dalla shell Korn è:
$(<comando>)
La sostituzione dei comandi può essere annidata. (Es. ls $(ls $(ls)))
Esempio:
/home/rossi$ ELENCO=$(ls)
/home/rossi$ ELENCON=$(ls [0-9]*)
Assegna alla variabile ELENCO l’elenco dei file della directorycorrente e ad ELENCON quello dei file il cui nome inizia con unnumero (se ce ne sono).
La shell di Unix – p.47/196
Sostituzione dei comandi / A
Esempio:
/home/rossi$ rm $( find / -name "*.tmp" )
Elimina da tutto il filesystem i file con estensione tmp.
Una diversa sintassi (derivante dalla Bourne shell)
‘<comando>‘
Attenzione alla direzione degli apici!
Nel caso di sostituzioni annidate occorre fare precedere gli apici piùinterni da un backslash (simbolo di escape).
Questa sintassi è considerata obsoleta e mantenuta solo percompatibilità con le vecchie versioni.
La shell di Unix – p.48/196
Espansione aritmetica
La shell Bash offre come funzionalità built-in il trattamento diespressioni aritmetiche intere (vedi anche variabili intere).
Le espressioni aritmetiche si rappresentano come
$((<espressione>))
(esiste anche una forma alternativa $[<espressione>])
L’espressione è soggetta ad espansione di variabili, sostituzione dicomandi ed eliminazione di simboli superflui per il quoting.
La sostituzione aritmetica può essere annidata.
Se l’espressione aritmetica non è valida, si ottiene una segnalazionedi errore senza alcuna sostituzione.
La shell di Unix – p.49/196
Espansione aritmetica / A
Esempi
/home/rossi$ echo 13+23
13+23
/home/rossi$ echo $((13+23))
36
/home/rossi$ VALORE=$((13+23))
/home/rossi$ echo $VALORE+1
36+1
Per assegnare il risultato di un’espressione ad una variabile
let <varname>=<expr>
Si possono dichiarare variabili intere
declare -i <varname>
La shell di Unix – p.50/196
Espansione di percorso (globbing)
Se una parola contiene uno dei simboli speciali ‘*’, ‘?’ e ‘[’, vieneinterpretata come modello ed espansa con l’elenco, ordinatoalfabeticamente, di percorsi (pathname) corrispondenti al modello.
Se il modello non corrisponde ad alcun file la parola resta immutata.
Note:
1. L’espansione non riguarda i file nascosti, ovvero i file il cui nomeinizia con un punto (a meno che il punto non sia indicatoespressamente nel modello)
2. L’espansione non genera mai il backslash di separazione deipercorsi.
La shell di Unix – p.51/196
Espansione di percorso / A
Significato dei metacaratteri
* qualsiasi stringa, compresa la stringa vuota
? qualsiasi carattere (uno solo)
[a,bc] uno qualsiasi dei caratteri elencati
[a-z] uno qualsiasi dei caratteri nell’intervallo
[!set] tutti i caratteri non in set
Il trattino ‘-’ e la la parentesi quadra chiusa ‘]’ perdono il loro significatospeciale se compaiono in un elenco [...] in prima o ultima posizione.
Esempio:
/home/rossi$ rm par[]a]
rimuove i file par] e para (se presenti), altrimenti . . . .
La shell di Unix – p.52/196
Quoting
Il termine quoting deriva dal verbo inglese ‘to quote’ (citare) e fariferimento ad un meccanismo che inibisce l’espansione, rimuovendoil significato speciale che alcune parole e simboli hanno per la shell.
Si distinguono tre meccanismi di quoting, con funzionalità differenti:
carattere di escape (backslash) \
apici semplici ’
doppi apici ", o virgolette.
La shell di Unix – p.53/196
Escape e continuazione
Il carattere di escape “backslash” indica che il carattere successivonon deve essere visto come carattere speciale.Esempio:
/home/rossi$ rm par\*
rimuove il file con nome par*, senza espandere il modello.
Nota: Se ‘\’ è seguito immediatamente dal codice di interruzione diriga (newline) allora indica che il comando continua sulla rigasuccessiva
Attenzione a non lasciare spazi dopo ‘\’, altrimenti questo opereràsullo spazio.
La shell di Unix – p.54/196
Apici singoli
Una stringa racchiusa all’interno di una coppia di apici semplici (’)non è soggetta a nessuna espansione
’<text>’
Attenzione: non confondersi con l’apice inclinato nel modo opposto(‘), utilizzato per la sostituzione dei comandi.
Esempio:
/home/rossi$ A=prova
/home/rossi$ echo ’Nessuna espansione di $A oppure *’
Nessuna espansione di $A oppure *
/home/rossi$
La shell di Unix – p.55/196
Apici doppi
Una forma più lieve di quoting, che inibisce solo l’espansione dipercorso, si ottiene racchiudendo il testo tra apici doppi:
"<text>"
preserva il valore letterale di tutti i caratteri ad eccezione di $, ‘, e \
(questo opera come carattere di escape solo quando è seguito da $, ‘," e newline).
Permette di definire delle stringhe inserendovi variabili e comandi dasostituire.
/home/rossi$ echo "La variabile PWD ha\
> valore $PWD (ovvero $(pwd))"
La variabile PWD ha valore /home/rossi (ovvero ˜)
/home/rossi$
La shell di Unix – p.56/196
Suddivisione in parole
Una parola è una sequenza di caratteri che non sia un operatore oun’entità da valutare. È vista come un’entità atomica (es. argomentofornito a un programma).
I delimitatori di parole sono contenuti nella variabile IFS (InternalField Separator), che, contiene, per default, i valori predefiniti:<Spazio><Tab><newline> (ovvero <SP><HT><LF>).
Quindi la variabile IFS è molto importante per il funzionamentodella shell: non può mancare o essere vuota.
La suddivisione in parole non avviene per stringhe delimitate daapici singoli o doppi.
La shell di Unix – p.57/196
Standard Input, Output, Error
Per convenzione ogni programma UNIX comunica seguendo unanalogo schema di input/output, che comprende tre canali
- riceve l’input dallo standard input (stdin)- manda l’ouput allo standard output (stdout)- segnala gli errori sullo standard error (stderr)
Per default la shell associa stdin alla tastiera e stdout, stderr alloschermo del terminale utente.
Per esempio, il comando cat, in assenza di argomenti, legge dallostandard input e stampa sullo standard output.
Ridirezione e piping permettono di alterare il comportamentostandard.
La shell di Unix – p.59/196
Ridirezione
La shell permette di ridirigere stdin, stdout e stderr,connettendoli a generici file.
Ogni file aperto è identificato da un descrittore di file. I descrittoristandard sono:
0 = standard input;
1 = standard output;
2 = standard error.
L’utente può utilizzare altri descrittori di file, a partire da 3 (dallaBourne shell).
La shell di Unix – p.60/196
Ridirezione dell’input
La sintassi generale è:
command [n]< filename
Associa il descrittore n al file filename aperto in lettura. Se n èassente (forma più comune) filename è associato allo standard input(descrittore 0).
Esempio: I comandi
/home/rossi$ sort < elenco
/home/rossi$ sort 0< elenco
Visualizzano il contenuto del file elenco, riordinandone le righe (ilcomando sort riceve il file da ordinare dallo standard input). Ilsecondo è equivalente al primo (viene solo indicato esplicitamente ildescrittore dello standard input).
La shell di Unix – p.61/196
Ridirezione dell’output
La sintassi generale è:
command [n]> filename
Associa il descrittore n al file filename aperto in scrittura. Se n èassente (forma più comune) filename è associato allo standardoutput (descrittore 1).
Se il file da aprire in scrittura esiste già, viene sovrascritto (se inveceè attiva la modalità noclobber [comando set] i dati sono aggiunti alfile eventualmente esistente.)
Per forzare la sovrascrittura di un file, anche se noclobber è attiva,si può utilizzare l’operatore di ridirezione >|.
La shell di Unix – p.62/196
Ridirezione dell’output - Esempio
Il comando
/home/rossi$ ls > dir-content.txt
Crea il file dir.txt nella directory corrente e inserisce in questo l’elenco dei filedella directory corrente. Una forma equivalente è
/home/rossi$ ls 1> dir.txt
In ambedue i casi, se è attiva la modalità noclobber, la lista dei file delladirectory viene aggiunta a dir.txt. Per forzare comunque la sovrascrittura
/home/rossi$ ls 1>| dir.txt
Il comando
/home/rossi$ ls XtgEWSjhy * 2> errori.txt
crea il file errori.txt nella directory corrente e vi inserisce i messaggi dierrore generati da ls (ad esempio se il file XtgEWSjhy non esiste).
La shell di Unix – p.63/196
Ridirezione dell’output “in aggiunta”
La sintassi generale è:
command [n]>> filename
Associa il descrittore n al file filename aperto in scrittura. Sefilename esiste già, i dati sono aggiunti in coda.
Esempio:
/home/rossi$ ls >> dir.txt
aggiunge al file dir.txt, il contenuto della directory corrente
Si può usare, ad esempio, per aggiungere qualcosa di breve ad un filedi configurazione
/home/rossi$ cat >> ˜/.bashrc
alias rm=’rm -i’
ˆD La shell di Unix – p.64/196
Ridirezione simultanea
Bash consente la ridirezione simultanea di standard output e standarderror in un unico file:
command &> filename oppure command >& filename
(la prima delle due notazioni è preferita).
Non è possibile sfruttare questo meccanismo per appendereinformazioni ad un file esistente.
Esempio:
/home/rossi$ ls XtgEWSjhy * &> out-err.txt
Crea il file out-err.txt e vi inserisce il messaggio di errore causatodall’assenza del file XtgEWSjhy e l’elenco dei file della directorycorrente.
La shell di Unix – p.65/196
Ridirezione - esempi
/home/rossi$ sort < elenco > elenco ordinato
Riordina il contenuto del file elenco e memorizza il risultato nel fileelenco ordinato.
/home/rossi$ more filename 2> /dev/null
Il file /dev/null è un “buco nero” per i bit. L’effetto è di eliminarequalsiasi messaggio di errore.
/home/rossi$ ls * xyz 1> outfile 2> errfile
Ridirige standard output ed error su outfile e errfile
rispettivamente.
Esistono forme più complesse di ridirezione . . .
command n >& m
il descrittore di file n diviene una copia del descrittore di output m.La shell di Unix – p.66/196
Here document
Un’ultima forma di ridirezione è il cosiddetto “here document”
command << word
la shell copia in un buffer il proprio standard input fino alla linea cheinizia con la parola word (esclusa) e quindi esegue command
utilizzando questi dati copiati come standard input.
usato per fornire l’input “inline” ad un comando all’ interno di unoscript.
Esempio:
#!/bin/bash
mail $1 << ENDOFMAIL
La sua richiesta riguardante $2 e’ stata accetata.
Cordiali saluti
ENDOFMAIL
echo Mail sent to $1
La shell di Unix – p.67/196
Exit status
Ogni comando UNIX, al termine dell’esecuzione, restituisce unvalore numerico, detto exit status.
Tipicamente, un valore di uscita pari a zero è considerato indice diuna conclusione regolare del comando, senza errori di alcun genere.
Se l’exit status viene utilizzato in un’espressione booleana, siassimila zero a true e ogni altro valore a false.
#!/bin/bash
if mkdir $1 2>/dev/null; then
echo "Directory $1 creata"
else
echo "Errore: Directory $1 non creata"
fi
La shell di Unix – p.69/196
Comandi semplici
Comando semplice
[<var assign>] <command> <args> <redirs>
Esempio: A=1 B=2 myscript pippo > outfile.txt
Sequenza (opzionale) di assegnamenti a variabili <var assign>,seguiti da una lista di parole (la prima delle quali <command> èinterpretata come nome del comando), da eventuali ridirezioni<redirs> e infine da un carattere di controllo (newline o “;”).
L’exit status è quello del comando nel caso di terminazione normale,oppure è stabilito dalla shell . . .
La shell di Unix – p.70/196
Comando semplice/A
In caso di terminazione “anomala”:
comando non trovato: 127
file non eseguibile: 126
comando terminato dal segnale n: 128 + n
evento/comando Nome Segnale Numero Segnale
Ctrl-c SIGINT 2Ctrl-z SIGSTOP 19kill SIGTERM 15kill -9 SIGKILL 9
La shell di Unix – p.71/196
Pipeline
Pipeline
[!] <command1> [| <command2> ... ]
Sequenza di comandi (anche uno solo!) separati dal carattere di pipe“|”. Lo standard output di <command1> viene connesso, attraversouna pipe, allo standard input di <command2>, ecc.
Ogni comando è eseguito in un processo differente (in unasottoshell). La pipeline termina quando termina ogni comandocoinvolto (Nota: la terminazione di un comando può causare laterminazione del precedente che tenti di scrivere su una pipe chiusa).
L’exit status è quello dell’ultimo comando della pipeline (o la suanegazione logica se la pipeline è preceduta da !).
La shell di Unix – p.72/196
Pipeline - esempi
Utilizzate spesso in connessione con i filtri, come more, grep, sort, sed,awk . . .
/home/rossi$ who | tee who.capture | sort
Mostra la lista ordinata degli utenti collegati. La lista non ordinataviene scritta nel file who.capture.
$ ps -aux | grep rossi
Mostra la lista dei processi relativi all’utente rossi.
$ echo "Vedo $(($(ls -l | wc -l) - 1)) file"
Mostra il numero dei file nella working directory.
$ cat /etc/passwd | awk -F: ’{ print $1 }’ | sort
Lista ordinata degli username in /etc/passswd
if who | grep -q $1; then echo "Utente $1 connesso"; fi
La shell di Unix – p.73/196
Liste e comandi composti
Liste: Una lista è una sequenza di una o più pipeline separata da unodegli operatori ; &, && or ||, e possibilmente terminata da ;, &, o<newline>.
Una lista può essere raggruppata attraverso parentesi (tonde o graffe)per controllarne l’esecuzione.
L’exit status della lista corrisponde a quello dell’ultimo comandoeseguito della lista stessa.
La shell di Unix – p.74/196
Liste: Sequenze non condizionali
<command1>; <command2>
La shell esegue i comandi sequenzialmente: prima <command1> edalla terminazione di questo <command2> (‘;’ sostituisce <newline>).
L’exit status è quello di <command2>.
Esempio:
/home/rossi$ latex lucidi.tex ; dvips lucidi.dvi -o
Avvia in sequenza una serie di comandi per la compilazione di unfile latex e la generazione del un file postscript corrispondente.
Due frammenti di script equivalenti
ls ; echo "Ciao a tutti" ls
echo "Ciao a tutti"
nel secondo si sostituisce il punto e virgola con un newline.La shell di Unix – p.75/196
Liste: Comando in background
Ci torneremo dopo . . .
Specificato con:
<command> &
La shell esegue <command> in una sottoshell, senza attenderne laterminazione.
L’exit status è 0.
La shell di Unix – p.76/196
Operatore di controllo &&
command1 && command2
Esegue <command1>. Quindi <command2> è eseguito se e solo se<command1> ritorna exit status 0 (true).
L’exit status è quello dell’ultimo comando eseguito, ovvero l’AND
logico (lazy) degli exit status dei due comandi.
Si usa spesso per eseguire command2 solo se command1 ha terminatoil suo compito con successo.
$ mkdir prova && echo "directory prova creata"
Esegue il comando mkdir prova. Se ha successo, esegue ilcomando successivo che visualizza un messaggio di conferma.
La shell di Unix – p.77/196
Liste: Operatore di controllo ||
command1 || command2
Esegue <command1>. Quindi <command2> è eseguito se e solo se<command1> ritorna exit status diverso da 0 (false).
L’exit status è quello dell’ultimo comando eseguito, ovvero l’OR
logico (lazy) degli exit status dei due comandi.
Si usa spesso per eseguire command2 solo se command1 non puòessere eseguito o riporta qualche tipo di insuccesso.
Esempi:
/home/rossi$ mkdir MyDir || mkdir MyDir1
Tenta di creare la directory MyDir e, solo se questa operazionefallisce, tenta di creare MyDir1.
La shell di Unix – p.78/196
Delimitatori di lista {...}
Una lista di comandi <list> può essere raggruppata tramite leparentesi graffe.
{ <list>; }
Esegue la lista nella shell corrente, senza creare alcuna subshell.L’effetto è dunque semplicemente quello di raggruppare più comandiin un unico comando (blocco).
Nota: Il ‘;’ finale è necessario, così come lo spazio tra la lista e leparentesi.
L’exit status è quello di <list>.
Esempio:
/home/rossi$ { date; pwd; } > out
scrive in out sia l’output di date che quello di pwd.La shell di Unix – p.79/196
Delimitatori di lista (...)
Una lista di comandi può <list> essere racchiusa tra parentesitonde.
( <list> )
Esegue la lista <list> in una subshell della shell corrente (quindiassegnamenti di variabili e comandi interni che influenzanol’ambiente della shell non lasciano traccia dopo che il comandocomposto è completato).
L’exit status è quello di <list>.
Esempio:
$ (cd Work && { mkdir Dir || mkdir Dir1; }) && echo "Ok"
Tenta di spostarsi nella directory Work e di creare le directory Dir oDir1. Se ci riesce, visualizza un messaggio di conferma.
La shell di Unix – p.80/196
Esecuzione in background
Secondo la filosofia multitasking di Unix, la shell permette dieseguire più di un programma contemporaneamente durante unasessione (job).
Con la sintassi
command &
il comando command viene eseguito in background:
il comando è eseguito in una subshell, di cui la shell non attendela terminazione. Quindi passa ad eseguire il comandosuccessivo;
l’exit status è sempre zero;
stdin non viene connesso alla tastiera (un tentativo di inputdetermina la sospensione del job).
La shell di Unix – p.82/196
Esecuzione in background / A
Esempio:
$ yes > /dev/null & echo "yes sta funzionando"
Esegue yes in background e visualizza il messaggio. Al terminedell’esecuzione della lista, yes è sempre attivo.
Tipicamente sono eseguiti in background job che richiedano tempodi computazione elevato e scarsa interazione con l’utente.
$ gzip lucidi.ps &
$ sort < file enorme > file enorme ord &
La shell di Unix – p.83/196
Controllo dei job / A
Il comando interno jobs fornisce la lista dei job della shell corrente:rossi@dsi:˜ > jobs
[1] Running emacs Lez3.tex &
[2]- Running emacs script.bash &
[3]+ Running xdvi Lez3.dvi &
rossi@dsi:˜ >
Il numero tra parentesi quadre è il numero del job all’interno dellashell (diverso dal process id PID, che identifica il processo nelsistema! [comando ps])
Il carattere + che segue le parentesi indica il “job corrente” (spostatoper ultimo dal foreground al background).
Il carattere - indica il penultimo job spostato dal foreground albackground.
La shell di Unix – p.84/196
Controllo dei job / B
Lo stato può essere:
Running: in esecuzione.
Stopped: sospeso pronto a tornare in azione appena qualcuno lorichieda.
Terminated: ucciso da un segnale.
Done: terminato con exit status 0.
Exit: terminato con exit status diverso da 0.
Con jobs -l vengono visualizzati anche i PID dei job
rossi@dsiII:/home/rossi/LEZIONI > jobs -l
[1] 20647 Running xemacs lez8.tex &
[2]- 20650 Running xemacs serve.tex &
[3]+ 20662 Running xdvi lez8.dvi &
rossi@dsiII:/home/rossi/LEZIONI >
La shell di Unix – p.85/196
Controllo dei job / C
Il comando interno kill consente di eliminare un job o un processo(specificato dal suo PID).
kill [-l] [-signal] { PID | %job }+
L’opzione -l fornisce la lista dei segnali possibili, che possonoessere specificati anche con codici numerici. Ad es., SIGKILL è 9.
I processi possono proteggersi da tutti i segnali ad esclusione diSIGKILL.
Esempio:
rossi@dsiII:˜/LEZIONI > jobs -l
[1]- 20647 Running xemacs lez8.tex &
[2]+ 20650 Running xemacs serve.tex &
rossi@dsiII:˜/LEZIONI > kill -9 %1
[1]- Killed xemacs lez8.tex
La shell di Unix – p.86/196
Controllo dei job / D
Per fare riferimento ad un job si utilizza il carattere %.
%n job numero n
%<prefisso> job il cui nome inizia con prefisso
(errore se ve n’è più d’uno)
%?<stringa> job la cui riga di comando contiene stringa
(errore se ve n’è più d’uno)
%+ (oppure %%) job corrente della shell (marcato con +)
%- job marcato con -
La shell di Unix – p.87/196
Controllo dei job / D
I job sospesi possono essere gestiti con i comandi interni bg e fg.
bg riattiva in background l’esecuzione di un job sospeso. Senzaargomenti si riferisce al job corrente (marcato con +).
fg riporta in foreground l’esecuzione di un job. Senza argomenti siriferisce al job corrente (marcato con +).
Con il carattere di controllo Ctrl-z il job in foreground viene sospeso(SIGSTOP).
Con il carattere di controllo Ctrl-c il job in foreground vieneinterrotto (SIGINT).
La shell di Unix – p.88/196
Controllo dei job - esempio
rossi@dsiII:˜/$ jobs
[2] Running emacs Lezione3.tex &
[3]- Running gv Lezione3.ps &
[4]+ Running ./GenPS.bash Lezione3 &
rossi@dsiII:˜/$ fg %2
emacs Lezione3.tex
ˆZ
[2]+ Stopped emacs Lezione3.tex
rossi@dsiII:˜/$ bg
[2]+ emacs Lezione3.tex &
rossi@dsiII:˜/$ fg
emacs Lezione3.tex
ˆC
rossi@dsiII:˜/$
La shell di Unix – p.89/196
Controllo dei job - altri comandi
ps [<opts>]
mostra i processi esistenti ed il loro stato
nohup <command>
esegue il comando rendendolo immune dai segnali HUP (hangup) eTERM (terminate), di modo che il comando non termini allaterminazione della shell.
wait <pid>
Sospende la shell fino alla terminazione del figlio con PID
specificato.
sleep <n>
Attende per un tempo specificato da n.
La shell di Unix – p.90/196
Accesso alle variabili
Alcune modalità di accesso alle variabili permettono di verificare se questesono definite (non vuote) e di specificare valori / azioni di default
$<var> o ${<var>} ritorna il valore di <var>
${<var>:-<val>} se <var> è vuota ritorna <val>
${<var>:=<val>} se <var> è vuota ritorna <val>e assegna tale valore alla variabile
${<var>:?<mesg>} se <var> è vuota scrive il messaggio<mesg> su stderr
${<var>:+<val>} se <var> non è vuota restituisceil valore <val>
La shell di Unix – p.92/196
Sottostringhe
È possibile selezionare una sottostringa del valore di una variabile con
${<var>:<offset>}
${<var>:<offset>:<length>}
che ritorna la sottostringa di $<var> che inizia in posizione <offset>(Nota: il primo carattere occupa la posizione 0).Nella seconda forma la sottostringa ha lunghezza <length> caratteri.
Esempio:/home/rossi$ A=armadillo
/home/rossi$ echo ${A:5}
illo
/home/rossi$ echo ${A:5:2}
il
La shell di Unix – p.93/196
Lunghezza
L’operatore
${#<var>}
consente di ottenere la lunghezza, in caratteri, del valore della variabile<var> (Nota: La lunghezza è comunque una stringa).
Esempio:/home/rossi$ A=armadillo
/home/rossi$ echo ${#A}
9
/home/rossi$ echo ${A:$((${#A}-4))}
illo
/home/rossi$ B=${A:3:3}
/home/rossi$ echo ${#B}
3
La shell di Unix – p.94/196
Pattern matching
È possibile selezionare parti del valore di una variabile sulla base di unpattern (modello). I pattern possono contenere i caratteri *, ? e [] e sonoanaloghi a quelli visti per l’espansione di percorso.
${<var>#<pattern>}
${<var>##<pattern>}
Se <pattern> occorre all’inizio di $<var>, ritorna la stringaottenuta eliminando da $<var> la più corta / più lunga occorrenzainiziale di <pattern>.
${<var>%<pattern>}
${<var>%%<pattern>}
Se <pattern> occorre alla fine di $<var>, ritorna la stringa ottenutaeliminando da $<var> la più corta / più lunga occorrenza finale di<pattern>.
La shell di Unix – p.95/196
Pattern matching - Esempio
outfile=${infile%.pcx}.gif
Rimuove (l’eventuale) estensione “.pcx” dal nome del file eaggiunge “.gif”. (Es. trasforma foto.pcx o foto in foto.gif).
basename=${fullpath##*/}
Rimuove la più lunga parte iniziale di fullpath che termini con “/”.In altri termini estrae il nome del file da un path completo.
dirname=${fullpath%/*}
Rimuove la parte finale più corta di fullpath che inizi con “/”.Ovvero estrae il nome della directory dal path completo di un file.
/home/rossi$ fullpath=/home/rossi/dir/myfile.txt
/home/rossi$ echo ${fullpath##*/}
myfile.txt
/home/rossi$ echo ${fullpath%/*}
/home/rossi/dir
Rimuovere l’ultimo elemento in PATH?
La shell di Unix – p.96/196
Sostituzione di sottostringhe
È possibile sostituire occorrenze di un pattern nel valore di unavariabile.
${<var>/<pattern>/<string>}
${<var>//<pattern>/<string>}
L’occorrenza più lunga di <pattern> in $<var> è sostituita con<string>. Nella prima forma sostituisce solo la prima occorrenza,nella seconda forma tutte le occorrenze. Se <string> è vuotaelimina le occorrenze incontrate.
Se il primo carattere del pattern è # l’occorrenza deve trovarsiall’inizio della variabile, se è % deve trovarsi alla fine.
Se <var> è @ oppure * l’operazione è applicata ad ogni parametroposizionale, e viene ritornata la lista risultante.
La shell di Unix – p.97/196
Esercizio: pushd e popd
Le funzioni pushd e popd implementano uno stack di directory.
pushd <dir> cambia la working directory, che diviene <dir>, e la salvasullo stack.
popd estrae la directory sul top dello stack e si sposta nella nuovadirectory top.
Esempio:/home/rossi$ pushd Work/Didat
/home/rossi/Work/Didat /home/rossi
/home/rossi/Work/Didat$ popd
/home/rossi
/home/rossi$
Si vogliono implementare questi comandi (builtin in bash) come funzioni.
La shell di Unix – p.98/196
Esercizio: pushd e popd / A
Lo stack viene implementato come una variabile DIRSTACK checontiene le varie directory, divise da uno spazio.
pushd <dir> si sposta nella directory argomento <dir> e lainserisce nello stack. Alla prima chiamata crea lo stack inserendovianche la directory corrente.
pushd ()
{
DIRNAME=${1:?"missing directory name."}
cd $DIRNAME &&
DIRSTACK="$PWD ${DIRSTACK:-$OLDPWD }"
echo "$DIRSTACK"
}
La shell di Unix – p.99/196
Esercizio: pushd e popd / B
popd rimuove la directory dal top dello stack e si sposta nella nuovatop directory.
popd ()
{
DIRSTACK=${DIRSTACK#* }
cd ${DIRSTACK%% *}
echo "$PWD"
}
Questa implementazione ha numerosi problemi (Es. non gestisce ogestisce male errori come directory non esistente, stack vuoto, . . . ,non consente di trattare directory che contengano uno spazio,implementa solo alcune funzionalità di pushd e popd, . . . ).
La shell di Unix – p.100/196
Strutture di Controllo
Le strutture di controllo permettono al programmatore di specificareche certe porzioni di codice devono essere eseguite o meno, oppuredevono essere eseguite ripetutamente, concordemente al verificarsi dicerte condizioni (riguardanti, ad esempio, il valore delle variabili).
Bash offre le strutture di controllo tipiche dei linguaggi diprogrammazione ad alto livello (imperativi).
Sono particolarmente utili per la preparazione di script di shell, mapossono essere usate anche nella riga di comando di una shellinterattiva.
La shell di Unix – p.102/196
Strutture di Controllo / A
Le strutture di controllo offerte da bash sono:
if-then-else
Esegue una lista di istruzioni se una condizione è / non è vera.
for
Ripete una lista di istruzioni un numero prefissato di volte.
while, until
Ripete una lista di istruzioni fino a che una data condizione divienefalsa / vera.
case
Esegue una lista di istruzioni scelta in base al valore di una variabile.
select
Permette all’utente di scegliere una tra le possibilità in una lista.
La shell di Unix – p.103/196
Costrutto if
Il costrutto if permette di eseguire liste di comandi differenti, in funzionedi condizioni, espresse anch’esse in forma di liste di comandi.
La sintassi è la seguente:
if <condition>; then if <condition>
<command-list> then
[elif <condition>; then <command-list>
<command-list>]... [elif <condition>
[else then
<command-list>] <command-list>]
fi ...
[else
<command-list>]
fi
dove <condition> e <command-list> sono liste di comandi,La shell di Unix – p.104/196
Costrutto if / A
Esegue la lista di comandi <condition> che segue if.
Se l’exit status è 0 (da interpretarsi come vero), esegue la<command-list> che segue then e quindi termina.
Altrimenti esegue ogni elif in sequenza, fino a trovarne uno la cuicondizione è verificata.
Se nessuna condizione si verifica, esegue la <command-list> chesegue else, qualora esista.
L’exit status è quello dell’ultimo comando eseguito (0 se non ne èstato eseguito alcuno).
La shell di Unix – p.105/196
Costrutto if / B
Dato che un exit status pari a 0 indica generalmente una conclusioneregolare del comando, un uso tipico del costrutto condizionale è
if <esecuzione regolare del comando>; then
<elaborazione normale>
else
<gestione dell’errore>
fi
Esempio: pushd con gestione (parziale) di directory non corrette
function pushd () {
DIRNAME=${1:?"missing directory name."}
if cd $DIRNAME; then
DIRSTACK="$DIRNAME ${DIRSTACK:-$OLDPWD }"
echo "$DIRSTACK"
else
echo "Error. Still in $PWD"
fi } La shell di Unix – p.106/196
Costrutto if / C
Esempio: Modificare il comando cd in modo che mostri le directory dipartenza e di arrivo, e fornisca l’exit status corretto.
function cd ()
{
builtin cd "$@"
es=$? # in es l’exit status di cd
echo "$OLDPWD --> $PWD"
return $es
}
return n consente ad una funzione di terminare esplicitamente conexit status n (in assenza di return la funzione ritorna l’exit statusdell’ultimo comando eseguito).
builtin permette di richiedere esplicitamente l’esecuzione delcomando cd builtin in bash.
La shell di Unix – p.107/196
Condizione - combinare exit status
Gli operatori &&, || e ! possono essere utilizzati come operatori logici(and, or, e negazione) per combinare exit status.
Verifica se un dato file contiene una tra due parole date.
file=$1
word1=$2; word2=$3
if grep $word1 $file || grep $word2 $file; then
echo "$word1 oppure $word2 sono in $file"
fi
Per verificare se ambedue le parole sono presenti nel file . . .
...
if grep $word1 $file && grep $word2 $file; then
echo "$word1 e $word2 sono in $file
...
La shell di Unix – p.108/196
Test
La condizione nel costrutto if è l’exit status di un comando(possibilmente composto). Questo non significa che if permetta diverificare solo se un comando conclude normalmente la sueesecuzione.
Bash offre un comando interno test
test <condition> oppure [ <condition> ]
che permette di effettuare vari controlli
proprietà dei file (esistenza, tipo, permessi, data)
confronti tra stringhe e interi
combinare logicamente condizioni.
La shell di Unix – p.109/196
Test - Stringhe
Bash permette di effettuare numerosi confronti tra stringhe. Eccoalcuni dei più comuni:
str1 = str2 str1 e str2 sono uguali
str1 != str2 str1 e str2 differiscono
str1 < str2 str1 è minore di str2
str1 > str2 str1 è maggiore di str2
-n str1 str1 è non nulla (lunghezza > 0)
-z str1 str1 è nulla (lunghezza = 0)
Per verificare l’uguaglianza di stringhe si può utilizzare anche “==”.
Gli operatori “<” e “>” si riferiscono all’ordinamento lessicografico.
La shell di Unix – p.110/196
Test - Stringhe / B
Esempio: Miglioramento di popd() con la gestione dell’errore di stackvuoto.
popd ()
{
DIRSTACK=${DIRSTACK#* }
if [ -n "$DIRSTACK" ]; then
cd ${DIRSTACK%% *}
echo "$PWD"
else
echo "stack empty, still in $PWD."
fi
}
La shell di Unix – p.111/196
Test - Attributi dei file
Alcuni test relativi alle proprietà dei file:
-e file file esiste
-d file file esiste ed è una directory
-f file file esiste e non è speciale (dir, dev)
-s file file esiste e non è vuoto
-r, -w, -x file hai diritti di lettura, scrittura, esecuzione su file
-O file sei l’owner del file
-G file un tuo gruppo è il gruppo di file
file1 -nt file2 file1 è più nuovo di file2
file1 -ot file2 file1 è più vecchio di file2
Nota: Con -nt e -ot si confronta la data di ultima modifica dei file.
La shell di Unix – p.112/196
Test - Stringhe e file: Esempio
Esempio: Realizzare uno script mygunzip che decomprima con gunzip unfile argomento, senza richiedere che questo abbia suffisso gz.
file=$1
if [ -z "$file" ] || ! [ -e "$file" ]; then
echo "Usage: mygunzip filename"
exit 1
else
ext=${file##*.} # determina il suffisso
if ! [ $ext = gz ]; then # e se non e’ "gz" lo aggiunge
mv $file $file.gz
file=$file.gz
fi
gunzip $file
fi
La shell di Unix – p.113/196
Test - Operatori logici
Diverse condizioni su stringhe e file possono essere combinateall’interno di un test, tramite gli operatori logici
-a (and) -o (or) ! (not)
All’interno di una condizione (test . . . oppure [ ...]) la sintassi è:
\( expr1 \) -a \( expr1 \)
\( expr1 \) -o \( expr1 \)
! expr1
La shell di Unix – p.114/196
Test - Esempio
Esempio: Miglioramento di pushd per la gestione della situazione in cui ladirectory specificata non è accessibile.
pushd ()
{ DIRNAME=$1
if [ -n "$DIRNAME" ] &&
[ \( -d "$DIRNAME" \) -a \( -x "$DIRNAME" \) ]; then
cd $DIRNAME
DIRSTACK="$DIRNAME ${DIRSTACK:-$OLDPWD }"
echo $DIRSTACK
else
echo "still in $PWD."; return 1
fi }
Si noti l’uso di && nella condizione dell’if che evita di verificare proprietàdi DIRNAME quando questa sia la stringa vuota.
La shell di Unix – p.115/196
Test - Interi
Bash consente di effettuare test su stringhe interpretate come valoriinteri:
-lt minore -gt maggiore
-le minore o uguale -ge maggiore o uguale
-eq uguale -ne diverso
Si noti che danno esito diverso dai confronti tra stringhe. Ad es. vale[ 6 > 57 ] ma non vale [ 6 -gt 57 ].
Sono utili quando si devono mescolare test su stringhe e su interi.Per operare solo su interi vi sono test più efficienti ed eleganti(((<cond>)) che include =, <, >, <=, >=, ==, !=, &&, ||).
La shell di Unix – p.116/196
Costrutto for
Permette di eseguire un blocco di istruzioni un numero prefissato divolte. Una variabile, detta variabile di loop, tipicamente riferita nelblocco, assume un valore diverso ad ogni iterazione.
Diversamente dal costrutto analogo dei linguaggi convenzionali, nonpermette di specificare quante iterazioni effettuare, ma una lista divalori assunti dalla variabile di loop.
Sintassi:
for <var> [ in <list> ]; do
<command-list>
done
Se in <list> è omessa, vale per default "$@", la lista degliargomenti dello script.
La shell di Unix – p.117/196
Costrutto for / A
Espande l’elenco <list>, generando una lista di elementi.
Esegue una scansione degli elementi in <list> (separatore: primocarattere in $IFS).
Alla variabile var è attribuito, ad ogni iterazione, un valore nellalista e quindi si esegue il blocco <command-list> (che conterrà,tipicamente, riferimenti a questa variabile).
L’exit status è quello dell’ultimo comando eseguito all’interno dellalista do, oppure 0 se nessun comando è stato eseguito.
La shell di Unix – p.118/196
Esercizio
Esercizio: Script rename old new che rinomina tutti i file con suffisso“.<old>" della directory corrente sostituendo il suffisso con ".<new>".
#!/bin/bash
OLD=$1
NEW=$2
for FILE in *.$OLD; do
mv $FILE ${FILE%$OLD}.$NEW
done
La shell di Unix – p.119/196
Esercizio
Esercizio: Implementare uno script che, analogamente a ls -R, mostriricorsivamente il contenuto delle directory fornite come agomento,evidenziandone, con spaziature, la struttura.
Esempio di outputdir1
subdir1
file1
...
subdir2
...
subsubdir1
...
subsubdir2
...
La shell di Unix – p.120/196
Esercizio / A
Vediamo in primo luogo uno script analogo a ls -R, senza struttura.
tracedir() {
for file in "$@"; do
echo $file
if [ -d "$file" ]; then
cd $file
tracedir $(command ls)
cd ..
fi
done }
Si noti che la funzione è ricorsiva. Inoltre con command ci si assicura chevenga eseguito il comando ls e non eventuali funzioni.
La shell di Unix – p.121/196
Esercizio / B
Soluzione dell’esercizio:
recdir() {
singletab="\t"
for file in "$@"; do
echo -e $tab$file # con -e, echo interpreta \t come
if [ -d "$file" ]; then # carattere di escape "<tab>"
cd $file
tab=$tab$singletab
recdir $(command ls)
cd ..
tab=${tab%"\t"}
fi
done }
Estensioni: distinguere directory vuote e file, stabilire una profonditàmassima di ricorsione, raffinare l’output.
La shell di Unix – p.122/196
Costrutto case
Consente di confrontare una stringa con una lista di pattern, e dieseguire quindi conseguentemente diversi blocchi di istruzioni.(simile a switch C, Java o case Pascal)
Sintassi:
case <expr> in
<pattern> )
<command-list> ;;
<pattern> )
<command-list> ;;
...
esac
La shell di Unix – p.123/196
Costrutto case / A
L’espressione (in genere una variabile) <expr> che segue case vieneespansa, e quindi confrontata con ognuno dei <pattern> (stesseregole dell’espansione di percorso) in sequenza (l’ordine èimportante).
Ogni <pattern> può in realtà comporsi di più pattern separati da |
<pattern1> | ...| <patternn>
ed è “soddisfatto” se lo è almeno uno tra <pattern1>, . . . ,<patternn>.
Se viene trovata una corrispondenza con uno dei pattern, la lista dicomandi relativa viene eseguita. Quindi si esce dal case.
L’exit status è quello dell’ultimo comando del blocco eseguito (0 senessun pattern combacia).
La shell di Unix – p.124/196
Esercizio
Scrivere una funzione che implementi il comando cd old new: cerca nelpathname della directory corrente la stringa old. Se la trova, la sostituiscecon new e cerca di spostarsi nella directory corrispondente.
cd() {
case "$#" in
0 | 1) builtin cd $1;;
2 ) newdir="${$PWD//$1/$2}"
case "$newdir" in
$PWD) echo "bash: cd: bad substitution" 1>&2
return 1 ;;
* ) builtin cd "$newdir" ;;
esac ;;
* ) echo "bash: cd: wrong arg count" 1>&2 ; return 1 ;;
esac }
La shell di Unix – p.125/196
Esercizio
Simulazione di un costrutto for dove la variabile assume valoriscalari in un range (for i=1 to n; do ... done)
Esempio: Script concat.sh file i j che concatena i file connome file-i, file-i+1, . . . , file-j, scrivendo il risultato in file.
#!/bin/bash
# non si gestisccono gli errori ...
FILE=$1
INF=$2 # seq i j: produce in output la sequenza
SUP=$3 # i, i+1, i+2, ..., j-1, j
for I in $(seq $INF $SUP); do
cat $FILE-$I >> $FILE
done
La shell di Unix – p.126/196
Esercizio
Realizzare uno script replace <str> [<rep>] [<ext>] che sostituisceogni occorrenza della stringa <str> con la stringa <rep> in tutti i file delladirectory corrente con estensione <ext>.Se <rep> è -m#, marca ogni occorrenza della stringa, inserendo all’inizioed alla fine un carattere #.
Se <ext> è assente, opera su tutti i file con suffisso txt.
La shell di Unix – p.127/196
Esercizio#!/bin/bash
[ $# -ge 2 ] || { echo "replace.sh: Bad usage"; exit 1; }
STR="$1" # prepara le stringhe da usare con sed
if [ "$2" = "-m#" ]; then
REP="#&#"
else
REP="$2"
fi
EXT=${3:-txt}
TMP=/tmp/replace$$.tmp
for FILE in *.$EXT; do
if [ -f $FILE ]; then
sed -re "s/$STR/$REP/g" $FILE > $TMP
mv $TMP $FILE
fi
doneLa shell di Unix – p.128/196
Costrutto select
Permette di generare in modo semplice un menù e quindi di gestirela scelta da tastiera dell’utente (non ha strette analogie con costruttidei linguaggi convenzionali)
Sintassi:
select <var> [ in <list> ]; do
<command-list>
done
L’elenco <list> che segue in viene espanso, generando una lista dielementi (se la lista è assente, per default si usa in "$@").
Ogni elemento di tale lista viene proposto sullo standard error,ciascuno preceduto da un numero. Quindi viene mostrato il promptnella variabile PS3 (per default è #) e chiesto un numero all’utente.
La shell di Unix – p.129/196
Costrutto select / A
Memorizza la scelta nella variabile built-in REPLY e l’elementocorrispondente della lista in <var>.
Se l’utente fornisce input vuoto, il menù viene riproposto. Una sceltanon valida viene comunque memorizzata in REPLY, mentre a <var>
viene assegnata la stringa vuota.
Esegue la lista di comandi <command-list> e quindi ripete l’interoprocesso.
Si esce dal costrutto select con il comando builtin break.
L’exit status è quello dell’ultimo comando eseguito, oppure 0 senessun comando viene eseguito.
La shell di Unix – p.130/196
Costrutto select / B
Esempio: Una funzione icd che elenca le directory presenti nella directorycorrente e, in base alla scelta dell’utente, si sposta in una di queste.
icd () {
PS3="Scelta? "
select dest in $(command ls -aF | grep "/"); do
if [ "$dest" ]; then
cd $dest
echo "bash: icd: Changed to $dest"
break
else
echo "bash: icd: $REPLY wrong choice"
fi
done
}
La shell di Unix – p.131/196
Costrutti while e until
Permettono di ripetere l’esecuzione di un blocco di istruzioni fino alverificarsi / falsificarsi di una condizione (simili ai costrutti deilinguaggi convenzionali).
Sintassi:
while <condition>; do until <condition>; do
<command-list> <command-list>
done done
Esegue la lista di comandi <command-list> fino a che <condition>è vera (0) / falsa (6= 0). La condizione <condition> è analoga aquella dell’if.
L’exit status è quello dell’ultima esecuzione di <command-list>,oppure 0 se non si entra nel ciclo.
La shell di Unix – p.132/196
Esempio / A
Elenca i percorsi indicati nella variabile PATH, uno per riga (si ricordi chein PATH i vari percorsi sono separati da “:”).
#!/bin/bash
path=${PATH%:}
declare -i I=0
path=${path#:}
echo "Le directory nel PATH sono:"
while [ "$path" ]; do
echo " $I) ${path%%:*}"
path=${path#*:}
I=$I+1
done
La shell di Unix – p.133/196
Esempio / B
Comando trycp <file> <dir>. Tenta di copiare il file <file> nelladirectory <dir>, e se fallisce riprova fino a riuscirci.
#!/bin/bash
if [ "$#" -ne 2 ]; then
echo "bash: trycp: wrong number of arguments."; exit 1
fi
file=$1; destdir=$2
if [ \( ! -d "$destdir" \) -o \( ! -e "$file" \) ]; then
echo "bash: trycp: Usage trycp <file> <dir>."; exit 1
else
until cp $file $destdir; do
echo "Attempt to copy failed. Waiting ..."; sleep 5
done
fi
La shell di Unix – p.134/196
Intro
Opzioni nella riga di comando
Attributi delle variabili, dichiarazioni
Array
gestione dell’I/O (comando read)
comando eval
La shell di Unix – p.136/196
Opzioni nella linea di comando
I comandi UNIX e così i nostri script / funzioni possono prevederedelle opzioni (con parametri propri).
command [-options] args
Le opzioni possono essere gestite con gli strumenti già visti. Ad es.,si supponga di voler scrivere uno script con sintassi
myscript [-o] file1 file2
Lo schema dello script
if [ $1 = -o ]; then
elabora opzione -o
1=$2; 2=$3
fi
codice che opera su $1 e $2
Illegale: I parametri posizionali sono read-only!
La shell di Unix – p.138/196
Opzioni nella linea di comando / B
Si può ovviare a questo problema con . . .
if [ $1 = -o ]; then
elabora opzione -o
file1=$2; file2=$3
else
file1=$1; file2=$2
fi
codice che opera su file1 e file2
Al crescere del numero di opzioni e di argomenti la gestione risultasempre più involuta, ed è ancora più difficile gestire il caso di unnumero arbitrario di argomenti
Es.: Provare rename -r <ext> <files>, che aggiunge l’estensione<ext> ai file elencati (in modo ricorsivo sulle sottodirectory con -r).
La shell di Unix – p.139/196
Comando shift
Il comando builtin shift di Bash permette di gestire piùagevolmente le opzioni.
shift [n]
Senza argomenti shift elimina il parametro posizionale $1 erinomina $2, $3, $4, . . . in $1, $2, $3 ($0 non è coinvolto)
1 ← $2, 2 ← $3, 3 ← $4, ...
{ shift-try }
Esempio: comando myscript [-o] file1 file2
if [ $1 = -o ]; then
elabora opzione -o
shift
fi
codice che opera su $1 e $2La shell di Unix – p.140/196
Comando shift / B
In generale shift n effettua lo shift di n posizioni: elimina iparametri posizionali $1, . . . , $n e rinomina $n+1, $n+2, . . .in $1, $2, . . .
1 ← $n+1, 2 ← $n+2, 3 ← $n+3, ...
Se n è 0, nessun parametro viene modificato.
Il valore di n deve essere un numero non negativo, minore o ugualeal numero di parametri $#. L’exit status sarà 0 in questo caso e 1
altrimenti.
La shell di Unix – p.141/196
Esempio
Scrivere uno script
cheap [-N] file
che, dato un file file nel quale ogni riga contiene una coppia <oggetto><valore>, stampa la lista ordinata degli N oggetti di minor valore in file.In assenza dell’opzione -N stampa i 4 oggetti più economici.
La shell di Unix – p.142/196
Esempio / B
#!/bin/bash
if [ -z ${1##-*} ]; then # se $1 e’ del tipo ’-...’
OPT=${1#-} # opzione e’ numerica ?
if [ -n "${OPT//[0-9]/}" ]; then
echo "The option must be a number."
exit 1
else
HEADOPT=$1
shift
fi
else
HEADOPT="-4" # se non e’ specificata l’opzione
fi # assegna il default
file=${1:?Missing file name.}
sort -k2 $file | head $HEADOPT
La shell di Unix – p.143/196
Opzioni Multiple: Schema
Esempio: Si supponga di voler realizzare uno script con opzioni multipledel tipo
myscript [-a][-b][-c] arg
Si potrà seguire lo schema:
while [ -z "${1##-*}" ]; do
case $1 in
-a ) elabora l’opzione -a ;;
-b ) elabora l’opzione -b ;;
-c ) elabora l’opzione -c ;;
* ) echo "Usage: myscript [-a][-b][-c] arg"
exit 1 ;;
esac
shift
done
elaborazione normale su $1
La shell di Unix – p.144/196
Opzioni con Argomenti: Schema
Esempio: Si supponga di voler realizzare uno script con opzioni multiplecon argomenti del tipo
myscript [-a][-b barg][-c] arg
Si potrà seguire lo schema:
while [ -z "${1##-*}" ]; do
case $1 in
-a ) elabora l’opzione -a ;;
-b ) elabora l’opzione -b
$2 e’ l’argomento dell’opzione
shift;;
-c ) elabora l’opzione -c ;;
* ) echo "Usage: myscript [-a][-b arg][-c] arg"
exit 1 ;;
esac
shift
doneLa shell di Unix – p.145/196
Comando getopts
Gli schemi discussi per la gestione delle opzioni presentano dellelimitazioni
non gestiscono opzioni multiple raggruppate dopo un solo ‘-’(ovvero la sintassi -abc per -a -b -c).
non gestiscono opzioni con argomenti non separati da uno spazio(ovvero la sintassi -barg per -b arg).
Gli schemi si possono complicare per prendere in considerazione questicasi . . .
La shell offre un comando builtin getopts per la gestione efficiente eflessibile di opzioni multiple con argomento.
La shell di Unix – p.146/196
Comando getopts / A
Il comando getopts, con sintassi
getopts <opt-string> <var>
è tipicamente usato come condizione del ciclo che esamina le opzioni.
Gli argomenti
<opt-string>: è una stringa contenente lettere che indicano leopzioni valide e ‘:’. Se una lettera è seguita da da ‘:’ allora l’opzionecorrispondente ha un argomento (separato o meno tramite spazidall’opzione stessa).
Es. Per le opzioni [-a] [-b arg] [-c] la stringa sarà "ab:c".
<var> è il nome di una variabile che conterrà l’opzione estratta.
La shell di Unix – p.147/196
Comando getopts / B
Funzionamento del comando
getopts <opt-string> <var>
estrae dalla linea di comando l’opzione (senza ‘-’) “corrente” e lamemorizza nella variabile <var>.
aggiorna la variabile builtin OPTIND che contiene l’indice delprossimo argomento da elaborare (la variabile OPTIND è inizializzataad 1 all’invocazione dello script).
se l’opzione richiede un argomento, lo memorizza in OPTARG
(aggiornando di conseguenza OPTIND).
l’exit status è 0 qualora ci siano ancora opzioni e 1 altrimenti.
La shell di Unix – p.148/196
Comando getopts - Errori
Qualora getopts incontri un’opzione non valida e quando mancal’argomento di un opzione, fornisce un messaggio di errore generalmentepoco informativo (es. cmdname: getopts: illegal option -x).
È possibile (e consigliato) gestire autonomamente gli errori, chiedendo agetopts un’informazione di errore silenziosa. Questo si ottiene facendoiniziare la stringa delle opzioni <opt-string> con ‘:’
Nel caso di
Opzione errata: Assegna ‘?’ a <var> e l’opzione errata a OPTARG.
Opzione senza argomento: Assegna ‘:’ ad <var> e l’opzione aOPTARG.
La shell di Unix – p.149/196
Comando getopts - Esempio
Es.: Comando con sintassi opts.sh [-a] [-b argb] [-c] cmdarg.
while getopts ":ab:c" opt; do
case $opt in
a ) echo "Option \"a"\" ;;
b ) echo "Option \"b\" with argument \"$OPTARG\"";;
c ) echo "Option \"c\"" ;;
: ) echo "Missing argument for option \"$OPTARG\"."
exit 1 ;;
\? ) echo "Wrong option \"$OPTARG\"!"
exit 1 ;;
esac
done
shift $(($OPTIND-1))
if [ -z "$1" ]; then echo "Missing argument!"
else echo "Cmd argument: $1"; fi
La shell di Unix – p.150/196
Attributi delle variabili
Finora abbiamo visto variabili il cui valore è una stringa di caratteri (equalche cenno di aritmetica intera).
Il comando Bash declare consente di verificare / assegnare / modificaregli attributi delle variabili.
declare [opt] var[=value]
Alcune opzioni comuni sono
-r var è read-only
-x var è esportata
-i var è intera
-a var è un array
Se non si specifica alcuna variabile, declare fornisce la lista dellevariabili esistenti, con le proprietà specificate da [opt].
La shell di Unix – p.152/196
Attributi delle variabili / A
readonly: Con
declare -r var (equiv. a readonly var)
si specifica che alla variabile var si può accedere solo in lettura (ilsuo valore non può essere modificato).
export: Con
declare -x var (equiv. a export var)
si specifica che var è variabile di ambiente (visibile alle sottoshell).
La shell di Unix – p.153/196
Attributi delle variabili / B
Altre opzioni del comando declare:
declare -f
Mostra solo le funzioni e le relative definizioni.
declare -F
Mostra solo i nomi delle funzioni, senza le relative definizioni.
declare -p var
Mostra tutti gli attributi della variabile var.
La shell di Unix – p.154/196
Variabili e funzioni
Le variabili dichiarate all’interno di una funzione sono locali allafunzione stessa. Ad esempio data la funzione
myfun() { declare A=modified ;
B=modified; }
si avrà
/home/rossi$ A=start; B=start
/home/rossi$ myfun
/home/rossi$ echo $A $B
start modified
Lo stesso effetto si ottiene con
local var
(che può essere utilizzato solo all’interno di una funzione). Array
La shell di Unix – p.155/196
Aritmetica intera
Bash permette di valutare espressioni aritmetiche intere
$(( <expr> ))
nelle quali le variabili possono non essere precedute da ‘$’.
Operatori aritmetici comuni
+ somma << e >> shift a sx, dx dei bit
- differenza & and (bit a bit)
* prodotto | or (bit a bit)
/ divisione intera ˜ e ! not (bit a bit e logico)
% resto ˆ x-or (bit a bit)
La shell di Unix – p.156/196
Aritmetica Intera / A
Operatori relazionali comuni
< maggiore > minore
<= maggiore o uguale >= minore o uguale
= uguale != diverso
&& and logico || or logico
Un’espressione relazionale intera dà come risultato
1 se è vera
0 se è falsa
(Attenzione: è la convenzione contraria rispetto agli exit status.)
La shell di Unix – p.157/196
Aritmetica intera / B
Come utilizzare un’espressione relazionale intera come condizione:
Tramite gli operatori -lt, -gt, -le, -ge, -eq, -ne all’internodel costrutto test. Ad es., [ 2 -gt 12 ] dà exit status 1 (falso).
Si può incapsulare un’espressione relazionale intera in un test:
[ $((2 > 12)) = 1 ]
Il modo più sintetico è quello di utilizzare il costrutto (( ...)).Data un’espressione aritmetica <expr>
((<expr>))
è un comando che ritorna exit status
0 (vero) se l’espressione è vera (diversa da 0)
1 (falso) se l’espressione è falsa (uguale a 0)
La shell di Unix – p.158/196
Aritmetica intera / C
Per assegnare un’espressione intera <expr> ad una variabile intera,dichiarata con declare -i varname, non occorre racchiuderla tra$(( ...)). Es.
/home/rossi$ declare -i intvar
/home/rossi$ intvar=3+2
/home/rossi$ echo $intvar
5
Per valutare espressioni aritmetiche ed assegnarle ad una variabile,Bash offre il costrutto let <var>=<expr>
/home/rossi$ let A=3+2
/home/rossi$ echo $A
5
Nota: let non crea variabili intere!La shell di Unix – p.159/196
Aritmetica intera - Esempio
Scrivere uno script ndu <dir> che per ogni argomento che sia una directory stampa lospazio utilizzato, in byte e Kbyte (se > 1Kb e < 1Mb) o Mbyte (se > 1Mb).(Nota: Si pùo specificare un numero in base diversa da 10 con Base#Numero).
for dir in ${*:-.}; do
if [ -d $dir ]; then
result=$(du -s $dir | cut -f 1)
let total=result*1024
echo -n "Total for $dir = $total bytes"
if ((total >= 16#100000)); then
echo " ($((total/16#100000)) Mb)"
elif ((total >= 16#400)); then
echo " ($((total/16#400)) Kb)"
fi
fi
doneLa shell di Unix – p.160/196
Array
Un array è una sequenza di elementi dello stesso tipo
Bash supporta array dinamici (di dimensione non specificata apriori), unidimensionali.
Un array può essere creato con una dichiarazione esplicita
declare -a elenco
oppure assegnando elementi alle sue componenti. Ad es.
elenco[2]=secondo
Gli array di Bash sono indicizzati da interi, con base a 0 (senzamassimo . . . in realtà fino a 599147937791). Quindi l’i+1-moelemento di un array A sarà riferito come A[i].
La shell di Unix – p.161/196
Array / A
Ogni elemento di un array può essere (sostanzialmente) visto comeuna normale variabile.
Ad esempio unset A[i] elimina l’elemento i-mo dell’array A.L’intero array viene eliminato con unset A.
Gli attributi di un array sono assegnati in modo globale. Ad esempiodeclare -i A[1] equivale a declare -i A: ogni elementodell’array diviene una variabile intera.
La shell di Unix – p.162/196
Array - assegnamenti
Esistono vari modi di assegnare valori agli elementi di un array. I tregruppi di assegnamenti che seguono producono lo stesso array:
A[0]=zero; A[2]=due; A[1]=giallo
Assegna i valori specificati agli elementi di indice 0,2 e 1.
A=(zero giallo due)
Assegna gli elementi elencati in sequenza, a partire dall’indice 0.
A=([1]=giallo due [0]=zero)
Si noti che dopo un assegnamento ad un indice specificato, isuccessivi proseguono in sequenza, fino a che non viene specificatoun nuovo indice.
La shell di Unix – p.163/196
Array - riferimenti
Per riferire il contenuto di una cella di un array A si usa la notazione
${A[Indice]},
dove Indice è una qualsiasi espressione aritmetica che produca unvalore non negativo.
Riferendo un array A come una normale variabile scalare si ottiene ilsuo primo elemento A[0].
È possibile espandere contemporaneamente tutti gli elementi(definiti) di un array utilizzando come indice * oppure @.
Per ottenere la lunghezza di un array A si possono usare le notazioni:
${#A[*]} oppure ${#A[@]}La shell di Unix – p.164/196
Array - esempio
Esempio:/home/rossi$ A=(zero giallo due [6]=sei)
/home/rossi$ echo ${A[1]}
giallo
/home/rossi$ echo $A
zero
/home/rossi$ echo ${A[@]}
zero giallo due sei
/home/rossi$ echo ${#A[*]}
4
La shell di Unix – p.165/196
Array - Esercizio
Realizzare uno script baseN.sh base num con due argomenti interi, che convertenum (interpretato come numero decimale) in base num (<= 10).
declare -i base=$1 numero=$2 num=$2 cont=0
declare -ia cifra
while ((num)); do
cifra[cont]=num%base
num=num/base
cont=cont+1
done
echo -n "Il numero $numero in base $base e’ "
cont=${#cifra[*]}
while ((cont)); do
echo -n ${cifra[cont-1]}
cont=cont-1
done
ReadLa shell di Unix – p.166/196
Gestione dell’Input/Output
I meccanismi per la gestione dell’I/O offerti da Bash possono esseresuddivisi essenzialmente in due classi:
Meccanismi di Ridirezione: permettono di gestire e modificare lesorgenti di input e la destinazione dell’output di comandi di shell eutilities.
Meccanismi per spostare dati tra file e variabili: Sostituzione dicomando $((...)), comandi per la gestione dell’I/O a livello dilinee e parole (echo, read).
La shell di Unix – p.168/196
Comando echo
Il comando echo visualizza sullo standard output i suoi argomenti,separati da uno spazio e terminati da <newline>.
echo [-n] [-e] [-E] <args>
Significato delle opzioni:
Opzione -n: sopprime il <newline> finale.
Opzione -e (-E): Abilita (disabilita) l’interpretazione dellesequenze di escape.
\a alert bell \b backspace
\c elimina ogni <newline> \E caratteri di escape
\n line feed (codice di interruzione di riga) \t tab orizzontale
\NNN carattere ASCII con codice ottale NNN \\ backslash
La shell di Unix – p.169/196
Lettura dell’input utente
Il comando builtin read permette di leggere l’input utente.
read <var1> <var2> ...<varn>
Legge una riga dallo stdin e la spezza in parole (secondo i separatoriin IFS). Assegna tali parole a <var1>, <var2>, . . . in sequenza.
Se il numero di parole eccede il numero di variabili, le paroledall’n-ma in poi sono assegnate a <varn>.
In particolare read <var> legge una riga dallo stdin nella variabile<var>.
Qualora non sia indicata nessuna variabile, per default assegna lalinea letta alla variabile REPLY.
La shell di Unix – p.170/196
Lettura dell’input utente / A
read -p <string> <var>
Stampa (sullo stderr) il prompt <string> prima di leggere la rigadallo stdin.
read -a <string> <var>
Vede la variabile <var> come un array e memorizza le parole lettesullo stdin, in successione, agli elementi dell’array, partendo daquello di indice 0.
read -r <var>
Ignora i caratteri di escape, che vengono interpretati letteralmente(‘raw’ input).
La shell di Unix – p.171/196
Lettura dell’input utente / B
read -n <nchar> <var>
Legge al più <nchar> caratteri e quindi termina.
read -t <timeout> <var>
Attende al più <timeout> secondi il completamento della riga diinput (-t sta appunto per ‘timeout’).
L’exit status di read è 0, a meno che non si incontri il carattere difine file o non si verifichi un timeout.
La shell di Unix – p.172/196
Elaborazione per linee
Il comando read consente di elaborare l’input linea per linea
while [ read LINE ]; do
elabora LINE
stampa la linea elaborata
done
In generale uno script del genere ha la stessa struttura e risulta menoefficiente di un programma analogo realizzato in un linguaggio ad altolivello (C, Java).
Tuttavia in alcuni casi, se il file da elaborare non è enorme e lamassimizzazione dell’efficienza non è fondamentale, può essere utileutilizzare script con questa forma.
La shell di Unix – p.173/196
Esempio
Si supponga che il file /etc/usrprompts contenga, per ogni utente, ilprompt desideratorossi \u@\h:\w\$
baldan [\u@\h:\w]
Si vuole scrivere un programma Bash che assegni alla variabile PS1 ilvalore corretto (es. porzione di /etc/profile).
PS1=’\u@\h’ # valore di default per il prompt
MYSELF=$(whoami)
while read -r USR PROMPT; do
if [ "$USR" = "$MYSELF" ]; then
PS1=$PROMPT
echo "L’utente $USR ha prompt $PROMPT"
break
fi
done
La shell di Unix – p.174/196
Esempio / A
Problema: Il codice descritto deve prendere l’input da /etc/usrprompts.
Creare uno script separato SetPrompt ed utilizzare gli operatori diridirezione non è una soluzione adeguata.
SetPrompt < /etc/usrprompts
Lo script è eseguito in una sottoshell e quindi la modifica dellavariabile PS1 non risulta visibile nella shell corrente.
Si può eseguire il codice nella shell corrente con “source” (o “.”)
source SetPrompt < /etc/usrprompts
Una soluzione alternativa consiste nell’utilizzare una funzionefunction SetPrompt () { <codice> } richiamata come
SetPrompt < /etc/usrprompts
La shell di Unix – p.175/196
Comandi multipli e ridirezione
Gli operatori di ridirezione possono essere applicati a definizioni difunzione ed a comandi composti.
Ridirezione nella definizione della funzione.
function Fun () { <codice> } < file
Ad ogni chiamata la funzione Fun prenderà l’input da file.
Ridirezione per costrutti di controllo.
while [ ... ]; do
...
done < file
La stessa tecnica si può usare per gli altri costrutti if ... fi,case ... esac, select ... done e until ... done.
La shell di Unix – p.176/196
Comandi multipli e ridirezione / A
Ridirezione di blocchi comandi
Anche per un comando composto { <list> } si possono utilizzaregli operatori di ridirezione già analizzati.
Ad esempio con
{
<command list>
} < file
tutti i comandi eseguiti nel blocco avranno lo standard input ridirettosu file.
La shell di Unix – p.177/196
Esempi
Le tecniche descritte offrono soluzioni alternative per SetPrompt . . .
Funzione SetPrompt:
function SetPrompt () { <codice> } < /etc/usrprompts
Ridirezione dell’input per il blocco while
PS1=’\u@\h’; MYSELF=$(whoami)
while read -r USER PROMPT; do
...
done < /etc/usrprompts
Creazione e ridirezione di un blocco di comandi:
{ PS1=’u@\h’; MYSELF=$(whoami)
while read -r USER PROMPT; do
...
done
} < /etc/usrprompts
La shell di Unix – p.178/196
Elab. della linea di comando
Per ogni linea di comando la shell:
1. Divide il comando in token, separati da metacaratteri (spazio, tab, newline,‘;’, ‘(’, ‘)’, ‘<’, ‘>’, ‘|’, ‘&’).
2. Se il primo token è una opening keyword, ad esempio if, while, . . . ,function, ‘{’ oppure ‘(’, il comando è composto. La shell effettua operazioniinterne opportune, quindi legge il comando successivo e ricomincia il processo dielaborazione.
3. Se il primo token è un’alias, lo espande e torna al punto 1 (quindi gli alias possonoessere ricorsivi e rappresentare parole chiave).
4. Esegue l’espansione delle graffe (es. a{b,c} diventa ab ac).
5. Esegue l’espansione della tilde (il simbolo ‘˜’ all’inizio di una parola diviene lahome directory dell’utente corrispondente).
La shell di Unix – p.180/196
Elab. della linea di comando / A
6. Esegue l’espansione delle variabili ( $var oppure ${...}).
7. Esegue l’espansione dei comandi $(cmd).
8. Esegue l’espansione delle espressioni aritmetiche $((...)).
9. Divide in parole il risultato delle espansioni delle variabili, dei comandi e delleespressioni aritmetiche, utilizzando come separatori i caratteri nella variabile IFS.
10. Esegue l’espansione di percorso (metacaratteri *, ?, [, ]).
11. Usa la prima parola come comando e ne reperisce il codice, cercando nel seguenteordine:
funzioni
builtin
eseguibili nelle directory indicate in PATH.
12. Esegue il comando.
La shell di Unix – p.181/196
Quoting
È possibile eliminare alcuni dei passi descritti tramite il quoting:
1. Apici singoli ’...’: Eliminano i passi 1–10. La stringa tra apici,immutata, è interpretata come una singola parola.
2. Apici doppi "...": Eliminano i passi 1–4 e 9–10. La stringa traapici, immutata, è interpretata come una singola parola (eliminal’espansione di alias, tilde, di percorso e la suddivisione in parole, edesegue l’espansione delle variabili, delle espressioni aritmetiche e lasostituzione di comando).
3. \<char>: inibisce l’interpretazione di <char> come simbolospeciale.
La shell di Unix – p.182/196
Funzioni, builtin e comandi
Quando una parola è interpretata come comando, la shell cerca prima tra lefunzioni, quindi tra i builtin ed infine tra gli eseguibili nel PATH.Per alterare questo ordine:
command <cmd>: Forza la shell a cercare <cmd> tra builtin ecomandi. Trascura alias, per side effect (in quanto il comando non èpiù la prima parola), e funzioni.
Esempio: Per ridefinire il comando rm tramite una funzione:
function rm () {
...
command rm
... }
builtin <cmd>: Forza la shell a cercare <cmd> solo tra i builtin.
La shell di Unix – p.183/196
Funzioni, builtin e comandi / A
Il comando enable permette di abilitare e di disabilitare i comandi builtindi Bash.
enable -n <builtin>
Disabilita il builtin <builtin> (senza argomenti elenca i builtindisabilitati).
enable <builtin>
Abilita il builtin <builtin> (senza argomenti elenca i builtinabilitati).
enable -a
Elenca tutti builtin, indicando quali sono abilitati / disabilitati.
La shell di Unix – p.184/196
Comando eval
Il comando eval permette di iterare l’elaborazione della linea dicomando eseguita dalla shell.
L’esecuzione di
eval <cmd>
consiste quindi di due fasi:
1. La shell elabora la linea di comando (come già spiegato)
2. Esegue eval, ovvero riesegue l’elaborazione della linea dicomando, e quindi esegue quanto ottenuto.
Molto potente: permette di costruire dinamicamente comandiall’interno di uno script e di eseguirli.
La shell di Unix – p.185/196
Esempi
/home/rossi$ alias lf=’ls -F’
/home/rossi$ LL=lf
/home/rossi$ $LF
bash: lf: command not found
/home/rossi$ eval $LF
Lezione6.ps bash.tex
Script highest <file> [<num>]: mostra le prime <num> righe di<file>, dopo averlo ordinato. In assenza di <num>, mostra tutto il file.
if [-n "$2" ]; then
sort $1 | head -$2
else
sort $1
fi
oppure, utilizzando eval
eval sort \$1 ${2:+"| head -\$2"}La shell di Unix – p.186/196
Esempio: make
Implementazione di un make “primitivo”, in grado di interpretare unsingolo costrutto del tipo:target : source1 source2 ...
commandlist
makecmd () {
read target colon sources
for src in $sources; do
if [ $src -nt $target ]; then
while read cmd; do
echo "$cmd"
eval $cmd
done
break
fi
done; }
La shell di Unix – p.187/196
Gestione dei segnali trap
Il comando trap permette di “catturare” e gestire i segnali inviati ad unprocesso.
trap [-p] [<cmd>] [<signal list>]
Se giunge uno dei segnali nella lista, esegue il comando <cmd>. Isegnali possono essere indicati in modo simbolico o numerico (Es.trap ’echo schiacciato Ctrl-c’ SIGINT).
Se l’argomento <cmd> è la stringa nulla
trap ’’ <siglist>
il segnale (o i segnali) verrà ignorato.
Senza argomenti o con l’opzione -p, fornisce la lista di comandiassociati con ciascun numero di segnale.
La shell di Unix – p.189/196
Gestione dei segnali trap / A
trap - <siglist> oppure trap <siglist>
Ripristina il gestore di ogni segnale in <siglist> al valore originale(il valore che avevano al momento dell’inizio della shell).
Per quanto riguarda le sottoshell:
I segnali intercettati sono riportati al loro valore originale nellesottoshell . . .
. . . ma i segnali ignorati in una shell sono ignorati anche nellesottoshell e qui non possono essere successivamente intercettatio inizializzati.
La shell di Unix – p.190/196
Opzioni per il debug
Alcune opzioni di Bash (comando set [-/+o]) utili per il debug:
opzione noexec (-n)Non esegue i comandi, ma ne verifica solo la correttezza sintattica(ad es. utile se lo script è molto complesso, e potrebbe esserepericoloso in presenza di errori).
opzione verbose (-v)Stampa ogni comando prima di eseguirlo (ad. es. utile per verificaredove si ferma uno script).
La shell di Unix – p.192/196
Opzioni per il debug / A
opzione xtrace (-x)Mostra il risultato dell’espansione prima di eseguire il comando.
$ ls
12 file1 23 file11
$ set -x
$ ls $(echo file?)
++echo file1
+ls -sF --color file1
12 file1
Il carattere ‘+’ nell’output, che indica il livello di espansione, è ilcontenuto della variabile PS4. Può essere personalizzata . . .
$ PS4=’xt ’
$ ls $(echo file?)
xxt echo file1
xt ls -sF --color file1
12 file1La shell di Unix – p.193/196
Segnali per il debug
Esistono due segnali generati dalla SHELL, che rappresentano eventiruntime di interesse per un debugger (umano o software che sia).
segnale EXIT
Generato all’uscita di uno script.
segnale DEBUG
Generato al termine dell’esecuzione di ogni istruzione. Ad es.,permette di ispezionare il valore delle variabili in parti critiche delprogramma:
function inspect () {
echo "La variabile XYZ vale $XYZ"
}
...
trap inspect DEBUG
<parte del codice che da’ problemi>
trap - DEBUGLa shell di Unix – p.194/196
Un debugger per bash
Il libro Learning the Bash shell (C. Newham and B. Rosenblatt -O’Reilly eds.) presenta un debugger per Bash bashdb non particolarmentesofisticato, ma funzionale (sorgenti nella pagina del corso).
Lanciato come bashdb testfile permette di
Specificare dei punti di stop nell’esecuzione del programma(breakpoint e break condition).
Eseguire un numero prestabilito di istruzioni del programma.
Esaminare e modificare lo stato del programma durante l’esecuzione.
Visualizzare il codice in esecuzione con indicazione dei breakpoint.
La shell di Unix – p.195/196
Un debugger per bash - Comandi
bp N: Fissa un breakpoint alla linea N
bp: Elenca i breakpoint e le break condition
bc string: Fissa string come break condition.
bc: Elimina la break condition.
cb: Elimina tutti i breakpoint.
cb N: Elimina il breakpoint alla linea N.
ds: Mostra lo script, con i numeri di linea ed i breakpoint.
g: Inizia/riprende l’esecuzione.
s [N]: Esegue N istruzioni (per default 1).
x: Attiva/disattiva la traccia.
h, ?: help.
! string: Passa string alla shell.
q string: Esce.
La shell di Unix – p.196/196