La shell di Unix - dsi.unive.itlso/Slides/bash-02-scripting.pdf · linguaggio di programmazione per...

196
La shell di Unix Uso interattivo e scripting Paolo Baldan [email protected] Dipartimento di Informatica Universit ` a di Venezia La shell di Unix – p.1/196

Transcript of La shell di Unix - dsi.unive.itlso/Slides/bash-02-scripting.pdf · linguaggio di programmazione per...

La shell di UnixUso interattivo e scripting

Paolo Baldan

[email protected]

Dipartimento di Informatica

Universita di Venezia

La shell di Unix – p.1/196

La shell bash

La shell di Unix – p.2/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

Personalizzazione di Bash

La shell di Unix – p.14/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 e funzioni(Intro)

La shell di Unix – p.30/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

La shell di Unix – p.38/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

Ridirezione

La shell di Unix – p.58/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

Combinare comandi

La shell di Unix – p.68/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

Controllo dei job(cenni)

La shell di Unix – p.81/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

Operatori su stringhe

La shell di Unix – p.91/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

Controllo del Flusso

La shell di Unix – p.101/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

Opzioni, array, input-output, eval(solo cenni)

La shell di Unix – p.135/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

Gestioni delle Opzioni

La shell di Unix – p.137/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

La shell di Unix – p.151/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

Input / Output

La shell di Unix – p.167/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

Dettagli sull’elaborazione della rigadi comando ed eval

La shell di Unix – p.179/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

La shell di Unix – p.188/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

Consigli per il debugging

La shell di Unix – p.191/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