Invio domanda da Silvano (Valeggio)

Filippi Stefano stefano63@gmail.com
Mar 10 Gen 2006 20:02:52 CET


Rimosso contenuto di tipo multipart/alternative-------------- parte successiva --------------

  
 pierluigi p
Membro Junior

Registrato il: Jul 2001
Provenienza: AQ-TE-RM
Messaggi: 398
ICQ : 

 La Shell Bash Bignamizzata 
dato che non si decidono a pubblicare (nei prossimi giorni) 
lo postiamo qui a puntate, questo con l'altro tutorial e' quello che ho fatto sotto natale perche' odio le feste e mi serviranno per lavoro. 


code:--------------------------------------------------------------------------------
+-------------------Programmazione di Shell bignamizzata----------------------+
				VER. 0.01
				31-12-2001
+-------------------------------Sommario--------------------------------------+
La shell Bash
I file di configurazione
Cenni sulle variabili
Cenni sui commenti
La variabile PATH
I prompts
Il file storico
Le variabili della posta
Varie
Gli alias
I permessi sui file
Introduzione alla programmazione in ambiente shell Bash
Il primo programma
Il secondo programma
Ancora commenti
Ancora variabili
Strutture di controllo
Il costrutto if
Il ciclo while
Il ciclo until
Il costrutto for
Il costrutto case
Le virgolette
Le operazioni con i numeri
Input dall'utente
Le funzioni
Intercettare i segnali di sistema
I costrutti AND e OR
Gli argomenti
Le redirezioni e le pipe
Files temporanei
Valori di ritorno
Le conclusioni

+---------------------------La shell Bash-------------------------------------+

Bash e' un acronimo di Bourne Again SHell, si tratta di una shell compatibile
con la shell Bourne, che e' stata una delle shell piu' usate in ambiente Unix 
alla quale aggiunge alcune migliorie mutuate anche dalle altre shell Unix.
E' in grado quindi di eseguire script scritti per la Bourne shell, mettendo a
disposizione al contempo costrutti e comandi piu' complessi non presenti nella
Bourne shell originale.
Non ultimo punto a suo favore consiste nel fatto di essere la shell
predefinita di quasi la totalita' delle distribuzioni Linux e di conseguenza
la piu' usata.
Per chi non lo sapesse, la shell e' un programma che agisce da intermediario
tra l'utente e il kernel, in pratica si occupa di tradurre i comandi
dell'utente e li passa al kernel per l'esecuzione. Il suo nome significa
conchiglia perche' appunto racchiude la sua perla, il kernel. Come quasi tutto
il software scritto per il sistema operativo Linux e' altamente configurabile.
+--------------------------I file di configurazione----------------------------+
La shell puo' usare cinque files di configurazione, ma spesso nelle varie 
distribuzioni non vengono usati tutti, d'altra parte vedremo che non e'
difficile crearsene di propri. I file sono:

	/etc/profile
	/etc/bashrc
	~/.bash_profile
	~/.bashrc
	~/.bash_logout

Fondamentalmente possiamo dividerli in due gruppi: file globali e file locali, 
cioe' quelli che contengono direttive di configurazione valide per tutti gli 
utenti, sono quelli che si trovano sotto la directory /etc, e quelli che 
contengono direttive di configurazione valide solo per l'utente che possiede 
la cartella nella directory /home che li contiene. Infatti nell'elenco sono
preceduti da un carattere tilde (~ che nei sistemi Unix e' un collegamento 
breve alla directory base dell'utente), e preceduti da un punto che li rende 
"invisibili" al comando ls senza l'argomento -a.
Se non dovessero essere presenti tutti nel sistema, non preoccupatevi, non 
tutti sono necessari, inoltre quelli necessari li creeremo in seguito.
Cominciamo a esaminarli uno ad uno:

/etc/profile

E' il file di configurazione globale che determina le variabili di ambiente 
e i programmi da eseguire per ogni utente che manda in esecuzione la shell.
Per fare un paragone con il mondo Dos potrebbe essere qualcosa di molto simile 
al file autoexec.bat.

/etc/bashrc

E' un file di configurazione globale molto simile al precedente, per questo 
motivo spesso non e' usato, contiene alias (collegamenti brevi a comandi molto 
lunghi li vedremo in seguito), e pezzi di codice nel linguaggio di scripting 
della shell che devono essere eseguiti alla partenza della shell. Tutto puo' 
essere spostato senza problema nel file /etc/profile.

~/.bash_profile

E' un file di configurazione locale che contiene direttive di configurazione 
e variabili di ambiente specifiche dell'utente al quale appartiene la 
directory /home in cui si trova. Il file viene letto ed eseguito 
successivamente a quelli globali e' modifica o sovrascrive variabili che 
riguardano esclusivamente l'utente.

~/.bashrc

E' un file di configurazione locale che contiene direttive di configurazione 
come gli alias, o funzioni definite dall'utente. Il file viene letto ed 
eseguito successivamente a quelli globali, gli alias o le funzioni saranno
specifici dell'utente e non influenzeranno nessun altro utente.
E' il corrispondente locale di /etc/bashrc.

~/bash_logout

E' un file di configurazione locale che contiene comandi da eseguire quando
l'utente esce dalla shell. I comandi influenzano solo l'utente che possiede 
la cartella /home nella quale si trova.

+----------------------------Cenni sulle variabili----------------------------+

Il metodo piu' usato per archiviare temporeneamente uno o piu' valori consiste 
nell'usare le variabili, cioe' usare un'area di memoria del computer 
assegnandogli un nome a cui si possa far riferimento in seguito per recuperare 
il valore assegnato, si puo' pensare a una variabile come una scatola chiamata 
per esempio mia_scatola, se noi mettiamo nella scatola una palla possiamo dire 
che il valore della variabile e' rappresentato dall'elemento palla.
Approfondiremo in seguito le variabili e il loro uso nello scrivere codice 
eseguibile dalla bash, per ora per la configurazione ci interessano solo due 
accezioni del termine cioe' le variabili di ambiente (environmental) e le
variabili locali (local). Le variabili di ambiente sono quelle variabili 
create autonomamente dal sistema operativo e normalmente sono definite nel file
/etc/profile , come per esempio SHELL, PS1, PS2, ecc. saranno discusse piu' 
avanti. Le variabili locali sono quelle variabili definite dall'utente e sono 
generalmente definite nel file ~/.bashrc che si trova come abbiamo visto nella 
/home dell'utente del quale possono influenzare l'ambiente.
La definizione di una variabile avviene in un modo intuitivo e elementare.
Consta di tre parti: il nome della variabile seguito dall'operatore di 
assegnamento "=" ,e il valore da assegnare, cioe':

nome_variabile=valore_variabile

Riutilizzando il paragone della scatola si potrebbe dire quindi che il valore 
della variabile mia_scatola=palla. Ponete attenzione al fatto che non ci sono 
spazi vuoti tra i componenti dell'assegnazione. Una volta definita la 
variabile la possiamo rendere disponibile ai programmi che l'utente usa con il 
comando export, cioe':

export nome_variabile

Per accedere al valore di una variabile cioe' per recuperarne il valore 
dobbiamo far precedere al nome variabile il suffisso "$". Per esempio per 
conoscere il valore della variabile SHELL possiamo digitare il comando: 

echo $SHELL

che produrra' una stampa a video

/bin/bash

In parole povere il nome_variabile sara' espanso nel valore_variabile quando 
il primo e' preceduto dal simbolo "$". Ritorneremo in seguito su come manipolare
una variabile, ma fin da ora appare chiaro che comprenderlo e' essenziale 
perche' tutti i file di configurazione del sistema sono scritte nel linguaggio 
della shell Bash.

+--------------------------Cenni sui commenti--------------------------------+

Se mentre editi un file di configurazione ti trovi davanti a una linea di 
codice dagli effetti dubbi invece di cancellarla puoi commentarla. Commentare 
e' l'azione di "rimuovere" una o piu' linee dall'esecuzione dello script 
facendo precedere la linea da commentare dal carattere "#" senza cancellare.
Chiaramente si puo' usare il carattere "#" anche per inserire commenti che 
migliorino la leggibilita' dello script, e che ci ricordino in seguito che
cosa abbiamo scritto.
Per esempio

echo "sono un testo di prova" #questo e' un commento esplicativo
#qui sotto c'e' un comando commentato che non verra' eseguito
#echo "sono un testo che non verra' visto"

La comodita' del commento rispetto alla cancellazione e che la linea puo'
essere ripristinata immediatamente.

+-------------------------La variabile PATH----------------------------------+

La prima variabile da configurare e' la variabile PATH che definisce le 
directory a cui possiamo accedere da qualsiasi punto del filesystem.
Per esempio potremmo avere un programma nella directory /bin chiamato
mio_prog, se la directory /bin e' presente nella variabile PATH possiamo 
lanciare il programma da qualsiasi posizione nel filesystem, altrimenti 
saremmo costretti o a digitare l'intero percorso /bin/mio_prog oppure portarci 
nella directory digitando prima cd /bin e successivamente ./mio_prog .
Il punto e la barra traversa (slash) che precedono il nome del programma 
dicono alla shell di cercare di eseguire quel file se ci sono i permessi
adatti. La variabile PATH e' definita in /etc/profile e ha una sintassi
leggermente diversa dal normale, ogni percorso di directory alla destra 
dell'operatore di assegnazione "=" è separata dal carattere ":" ,esempio: 

PATH=/bin:/usr/bin:/usr/local/bin
export PATH

rendera' possibile mandare in esecuzione da qualsiasi punto del filesystem
tutti gli eseguibili che si trovano nelle cartelle dichiarate nella variabile
PATH. Se noi volessimo aggiungere altri valori alla variabile PATH potremmo
digitare semplicemente:

PATH=$PATH:/usr/games
export PATH

che aggiungera' al valore gia' esistente della variabile PATH il nostro nuovo 
valore, infatti il nome variabile preceduto dal simbolo "$" verra' espanso con
il contenuto originale della variabile cioe' /bin:/usr/bin:/usr/local/bin che 
diventera' /bin:/usr/bin:/usr/local/bin:/usr/games. Si puo' modificare il
proprio path inserendo il comando precedente in ~/.bash_profile.
--------------------------------------------------------------------------------


 II° parte 

+------------------------------I prompts--------------------------------------+

Il prompt non e' altro che il sistema in attesa di input dall'utente, la sua
forma classica e':

nome_utente@nome_computer:[#,$]

il mio in questo momento appare come

root@darkstar:~#

e mi sta' dicendo che sono l'utente "root" sulla macchina "darkstar" e mi
trovo nella mia home directory "~", se fossi stato un semplice utente al posto 
del "#" ci sarebbe stato probabilmente un "$". Dico probabilmente perche'
quello che vediamo riflette i valori della variabile PS1 definita nel file 
/etc/profile che e' di proprieta' dell'utente root, se noi volessimo un prompt 
diverso potremmo ridefinire la variabile PS1 nel file ~/.bash_profile.
Questa variabile accetta dei valori predefiniti che sono ottenuti facendo
seguire alla barra rovesciata (backslash) dei caratteri speciali che sono:

	\t	l'ora corrente nel formato HH:MM:SS 
	\d	la data in formato esteso es. "Tue May 18"
	\n	un carattere di nuova linea
	\s	il nome della shell
	\w	la directory corrente
	\W	il percorso completo alla directory corrente
	\u	il nome dell'utente
	\h	il nome della macchina
	\#	il numero del processo associato al comando in esecuzione
	\!	la posizione numerica nel file storico dei comandi
	\$	se l'UID e' 0 mostraun "#", altrimenti ub "$"

Quindi inserendo opportunamente questi caratteri possiamo modificare
l'aspetto del nostro prompt. Se assegno alla variabile PS1 i seguenti valori:

PS1="[\u@\h \W]\$" 

il mio prompt mi apparira' cosi':

[root@darkstar /root]#

se modifico la variable cosi':

PS1="[\t \s]\$ " 

il mio prompt mi apparira' cosi':

[12:18:24 bash]#

Oltre a questi caratteri speciali la variabile PS1 puo' contenere anche comandi
per esempio se volessi far apparire la versione del kernel:

PS1="`uname -r` \$ "

il mio prompt mi apparira' cosi':

2.4.17#

Attenti all'uso delle virgolette (che avranno un paragrafo tutto per loro), ho 
usato il carattere "`" non le classiche virgolette "'", chiamato backticks o
virgolette rovescie,ottenute con AltGr+virgoletta semplice .
La variable PS2 determina l'aspetto del prompt secondario e viene visualizzato
quando si digitano comandi incompleti. Questo succede quando abbiamo a che
fare con comandi molto lunghi, possiamo digitare il comando su piu' linee 
facendo precedere il comando invio da una barra rovesciata (backslash), a 
quel punto la shell capira' che non abbiamo terminato e ci presentera' il
prompt secondario che significa che sta attendendo successivi comandi.
Se per sempio vogliamo vedere come si presenta il prompt secondario nella
nostra shell possiamo digitare

root@darkstar:#if [ -f /etc/profile ]; then

poi premiamo invio e ci accorgeremo di avere un prompt diverso il mio e':
>
in questo caso non abbiamo dovuto far precedere l'invio da una barra rovescia
perche' la shell riconosce i suoi costrutti di programmazione, volendo si 
potrebbe digitare un semplice

echo $PS2

per ottenere direttamente il valore della variabile. Tutto quello che e' stato
detto per PS1 vale in linea di massima per PS2.

+------------------------Il file storico-------------------------------------+

La shell e' in grado di ricordare i comandi immessi dall'utente che normalmente
sono salvati nel file ~/.bash_history e possono essere richiamati premendo i
tasti freccia in su' e freccia in giu', questo comportamento puo' essere 
modificato configurando le variabili:

HISTSIZE 
HISTFILE 
HISTFILESIZE 

E' da ricordare che per abitudine le variabili di ambiente vengono indicate
usando lettere maiuscole a differenza delle locali, per cui si usani le
minuscole. Queste variabili sono definite in /etc/profile ma possono essere
sovrascritte in ~/.bash_profile.

HISTSIZE

Vero il numero massimo dei comandi da memorizzare nel file storico 
normalmente il valore preimpostato e' 500.

HISTFILE

Indica il file che deve essere usato per contenere i comandi digitati
normalmente il valore preimpostato e' ~/.bash_history, puo' anche non essere
impostato, lo storico si limitera' alla sessione di lavoro corrente, puo' 
essere rediretto con una pipe su /dev/null (vedremo in seguito).

HISTFILESIZE 

Determina la grandezza fisica massima che puo' avere il file dello storico.

+-------------------------Le variabili della posta----------------------------+

La shell ha le seguenti variabli che influenzano le funzioni di posta

MAIL 
MAILCHECK 
MAILPATH 
MAIL_WARNING 

Queste variabili sono normalmente definite nel file /etc/profile ma possono
essere sovrascritte nel file ~/.bash_profile .

MAIL

Quando un messaggio di posta arriva all'utente, il suo contenuto e' scritto su
di un file, questo file e' definito dalla variabile MAIL che normalmente 
contiene il valore /var/spool/mail/nome_utente . Si puo'cosi' indicare alla
shell il file da controllare per l'arrivo di nuovi messaggi.

MAILCHECK

Questa variabile definisce l'intervallo di tempo che deve trascorrere prima
che la casella di posta locale venga controllata. Il valore preimpostato e'
60 che significa che la shell controllera' la directory ogni minuto.

MAILPATH

Questa variabile definisce il percorso per raggiungere le directory di posta.
Normalmente contiene il valore /var/spool/mail . Puo' essere usata anche per
personalizzare il messaggio che notifica l'arrivo della nuova posta, esempio:

MAILPATH='/var/spool/mail/nome_utente "Hai posta....!"' 
MAIL_WARNING

Se questa variabile e' definita, la shell vi informera' con un messaggio, se
state leggendo un messaggio gia' letto in precedenza. Visto l'uso ormai 
consolidato dei client di posta come pine, mutt, o altri grafici questa 
variabile e' ormai poco usata.

+-------------------------------Varie-----------------------------------------+

Ci sono direttive e variabili che non sempre sono usate, di seguito brevemente 
le piu' frequenti.

set -o notify

ci informa che un programma che avevamo avviato in background, facendo cioe' 
seguire il nome comando dal carattere "&", e' terminato stampando il messaggio

[1]1+ Done nome_comando 

Puo' essere usata in /etc/profile oppure in ~/.bash_profile.

TMOUT
Questa variabile specifica quanto tempo la shell attendera' per l'inserimento
del nome utente o della password al login.

TMOUT=60

attendera' un minuto prima di reinizializzare il login.

set -o noclobber

Questa direttiva impedira' la redirezione dell'output su di un file gia'
esistente. Per aggirare questa limitazione il carattere di redirezione dovra'
essere seguito da un carattere di pipe (approfondito in seguito). Esempio:

root@darkstar:#echo "testo di prova" >| ~/.test_file

si evita in questo modo la sovrascrittura accidentale di un file.
Puo' essere usata in /etc/profile oppure in ~/.bash_profile, per revocare la 
direttiva possiamo usare il comando:

unset +o noclobber 

+-----------------------------Gli alias---------------------------------------+
Gli alias si possono definire come comandi "accorciati". Se vogliamo chiamare
un comando lungo in maniera piu' mnemonica possiamo assegnarlo ad un alias. 
Se per esempio volessimo evitare di digitare ogni volta il comando:

ls -aF --color

potremmo assegnarlo nel file /etc/bashrc oppure ~/.bashrc nel seguente modo:

alias ls='ls -aF --color'

Notare ancora una volta l'uso delle virgolette rovescie. Possiamo definire
quanti alias ci servono senza limiti.

+----------------------------I permessi sui file-----------------------------+

Una delle molte cose che differenzia un sistema Unix da windows sono i
permessi e il concetto di appartenenza di un file. Ogni file nello Unix e' di
proprieta' di un utente e di un gruppo. Gli utenti possiedono i file creati da
loro stessi, e quelli attribuiti all'utente dall'utente root con il comando
"chown". Il gruppo di un file viene dedotto dal sistema in base al gruppo di
appartenenza dell'utente, visto che un utente puo' appartenere a piu' gruppi 
puo' cambiare gruppo di appartenenza a un file con il comando "chgrp". Si puo'
sapere a che gruppo appartenga un utente usando il comando "groups".

chown [nome_utente] [nome_file]
chgrp [nome_gruppo] [nome_file]
groups [nome_utente] 

I permessi definiscono chi puo' fare qualcosa, e che cosa puo' essere fatto
con un file. Sono divisi in quattro gruppi, i primi tre di uso frequente, 
il quarto di uso meno frequente e piu' complesso, per cui ci limiteremo a una
sua descrizione meno approfondita. Il permesso sui gruppi definisce che cosa
un utente appartenente a quel gruppo puo' fare con il file, l' universale
quello che un utente non appartenente al gruppo puo' fare.
Ogni sezione della terna utente-gruppo-altri e' composto da tre bits che
quindi generano tre stati di settato/non_settato, i campi indicano:

lettura 	-il file puo' essere letto. In caso di directory e' possibile 
		 dare il comando ls nella directory. Viene indicato dal
		 carattere "r".
scrittura	-il file puo' essere scritto. In caso di directory e' possibile
		 creare o cancellare i file all'interno. Viene indicato dal
		 carattere "w".
esecuzione	-il file puo' essere mandato in esecuzione. In caso di
		 directory e' possibile usare il comando cd /nome_directory. 
		 Viene indicato dal carattere "x".

La rappresentazione numerica dei permessi non e' molto difficile. Le tre 
categorie utente-gruppo-altri, che contengono ciascuna tre permessi, vengono
considerate come una sequenza di tre cifre in formato ottale. Ogni cifra
ottale corrisponde a tre cifre in binario (base 2), che sono sequenze di uno 
e di zero, quindi abbiamo tre gruppi di tre cifre che ci permettono di settare
tutti i permessi. Dunque ogni cifra ottale ci restituisce tre cifre binarie,
per esempio la cifra ottale 6 corrisponde al numero binario 110 che quindi
imposta a 1 il permesso di lettura, a 1 il permesso di scrittura e a 0 quello
di esecuzione. Vi riporto per comodita' la tabella:

base 8 (ottale)		base 2 (binario)
	0			000
	1			001
	2			010
	3			011
	4			100
	5			101
	6			110
7 111
8 
Come appare piu' chiaro un numero ottale ha il corrispondente binario che puo'
essere interpretato come una terna di valori booleani. Ci possiamo ricavare
un'altra tabella:
permesso		numero			binario
   r			  4			  100
   w			  2			  010
   x			  1			  001
Dunque per determinare il numero totale da specificare per ogni elemento della
terna utente-gruppo-altri bastera' sommare i valori relativi. Ultimo esempio:
proprietario	rx	4+1=5
gruppo		r	4
altri		r	4
544 ovvero 101 100 100
digitando 
chmod 544 nome_file
gli attribuiremo i seguenti permessi
r-xr--r--
E' possibile impostare, ma solo per gli eseguibili e le directory anche i bit 
di suid e sgid che vengono usati per modificare il comportamento standard dei
permessi, ma possono causare problemi di sicurezza e meritano una discussione
approfondita che va al di la' delle intenzioni di questo tutorial.



 III° parte 

+----------Introduzione alla programmazione in ambiente shell Bash------------+

Come tutte le shell disponibili in Linux la shell Bash non solamente permette
all'utente di digitare i comandi, ma include nell'eseguibile un vero e proprio
mini linguaggio di programmazione, permettendoci di codificare script, cioe' 
piu' o meno brevi sequenze di istruzioni, da qui la definizione di linguaggio
di scripting. Molti dei programmi che normalmente si utilizzano in Linux sono
script di shell, quindi per comprendere il loro funzionamento ed eventualmente
modificarli, e' indispensabile conoscere le basi del linguaggio e la sua
sintassi. Prima di iniziare e' opportuno rimarcare la differenza tra linguaggi
di scripting e linguaggi compilati. Generalmente i programmi compilati sono 
molto piu' veloci e potenti dei programmi di scripting, ma altrettanto spesso
questa velocita' e potenza non giustifica la complessita' e il tempo
necessario per lo sviluppo. Un programma compilato viene generato da un file
di testo denominato sorgente, che viene appunto processato da un compilatore
che lo traduce in linguaggio macchina restituendoci un eseguibile binario (non
piu' un semplice file di testo ascii quindi), questo eseguibile dipende
pesantemente dalla piattaforma su cui e' stato compilato e non e' eseguibile
su altre piattaforme, esempio per tutti il linguaggio C. Una via di mezzo e'
rappresentata dai linguaggi semi-compilati come il Java in cui si ha solo una
precompilazione del sorgente di testo, che viene processato per ottenete il
bytecode un file binario che verra' eseguito dall'interprete Java installato
sul sistema. Un classico linguaggio interpretato, oltre allo shell scripting
ovvio, e' l'html in cui il sorgente e' e rimane un semplice file di testo, e'
l'interprete incorporato nel browser che si occupa di tradurre i caratteri 
o le sequenze di caratteri speciali nelle opportune istruzioni di 
visualizzazione. I programmi interpretati sono dunque piu' lenti, vista la
necessaria interpretazione "al volo" ma sono facilmente e velocemente
modificabili, e sono ottimi per le piccole applicazioni. Altri esempi di
linguaggi interpretati sotto Linux sono il Perl, il Lisp, il Tcl,ecc. .
Per scrivere semplici programmi interpretabili dalla shell e' richiesta
la conoscenza di almeno i principali comandi di Linux, e la capacita' di usare
un editor di testo, meglio se non visuale come vim, pico o mcedit. E' anche
buona abitudine non provare i propri script come utente root per evitare
spiacevoli danni a file di sistema

+--------------------------Il primo programma--------------------------------+

Per non infrangere la tradizione cominceremo con un programma che stampa a
video l'ignobile frase "Ciao mondo". Quindi aprite un editor di testo e 
scrivete:

#!/bin/bash
echo "Ciao mondo"

la prima riga avvisa Linux che vogliamo che tutto cio' che segue sia passato
all'interprete bash, che in questo caso si trova nella directory /bin ma che
puo' trovarsi in un posizione non standard che possiamo indicare sotto forma di 
percorso. Il prossimo passo sara' salvare lo script col nome di ciao.sh, 
successivamente dovremo renderlo eseguibile settando i dovuti permessi, quindi

root@darkstar:#chmod 700 ./ciao.sh 

io lo sto facendo da root ma voi entrate nel sistema come utente.
Una volta settate i permessi possiamo avviare il nostro programma semplicemente
Digitando

root@darkstar:#./ciao.sh 

Il vostro primo programma vi salutera', e tutto questo ci servira' a ricordare
la sequenza, scrittura-salvataggio-permessi, che sta' alla base di tutto.
Cosa ha fatto il programma? Ha semplicemente stampato a video la scritta
Ciao mondo, ma come ha fatto? Ha semplicemente usato un comando di shell, il
comando "echo" che prende un argomento e lo stampa a video. Un argomento e'
qualsiasi stringa o numero che segue il comando, in questo caso la stringa
"Ciao mondo" e' l'argomento passato al comando "echo". Quando noi dalla shell
digitiamo il comando ls /var/log passiamo al comando "ls" l'argomento
"/var/log". Lo stesso effetto del nostro programma, vista la sua semplicita' 
era ottenibile da shell digitando

root@darkstar:#echo "Ciao mondo"

ma era solo a scopo didattico.

+--------------------------Il secondo programma------------------------------+

Scriveremo un programma un tantino piu' utile, spostera' tutti i file della
directory corrente dentro una cartella che creeremo, cancellera' la directory
creata e i file al suo interno, e alla fine la ricreera'. Tutto cio' sarebbe
possibile da shell digitando i seguenti comandi:

mkdir trash
mv * trash
rm -rf trash
mkdir trash

ma noi metteremo tutti i comandi in un file di shell, quindi apriamo l'editor
e digitiamo :

#!/bin/bash
mkdir trash
mv * trash
rm -rf trash
mkdir trash
echo "file cancellati e directory ricreata con successo" 

Salviamolo con il nome di test.sh, diamogli i permessi con il chmod 700, ed
eseguiamolo digitando ./test.sh .
Quindi anche se siamo agli inizi se dobbiamo compiere azioni ripetitive
consideriamo la possibilita' di scriverci un file di shell.

+----------------------------Ancora commenti-----------------------------------+

Abbiamo visto precedentemente che i commenti vengono ottenuti facendo 
precedere come primo carattere della linea il simbolo "#", l'unica eccezione
e' rappresentata dall'invocazione dell'interprete nella prima riga di ogni
script cioe' la sequenza #!/bin/bash . Fate uso dei commenti il codice sara'
piu' chiaro per voi e per gli altri.

+-----------------------------Ancora variabili---------------------------------+

Abbiamo gia' detto che le variabili sono fondamentalmente "scatole" che 
contengono valori. Avremo bisogno spesso di crearci variabili per  motivi
diversi, per memorizzare un valore immesso dall'utente, per effettuare
calcoli o per memorizzare stringhe. Per fare un esempio pratico aprite il
vostro editor e create un file di shell che contenga le seguenti righe:

#!/bin/bash
x=12
echo "Il valore della variabile x=$x"

Salvatelo dategli i giusti permessi ed eseguitelo.
Quello che abbiamo fatto e' stato assegnare alla variabile x il valore di 12, 
notate ancora una volta che non ci deve essere spazio tra i termini, il
valore della variabile puo' essere recuperato anteponendo il suffisso "$" al
nome della variabile. Le variabili possono essere sovrascritte e conterranno 
il valore dell'ultima assegnazione, per esempio

#!/bin/bash
x=12
echo "Il primo valore della variabile x=$x"
x="a"
echo "Il secondo valore della variabile x=$x"

+--------------------------Strutture di controllo----------------------------+

Le strutture di controllo permettono ai nostri programmi di prendere delle
"decisioni", di essere molto piu' compatti ed eleganti, e sopratutto di
gestire gli eventuali errori. Quello che abbiamo fatto finora e' fornire 
all'interprete una serie di istruzioni da eseguire. Se noi per esempio creiamo
un file di shell chiamato test1.sh che contenga i seguenti comandi

#!/bin/bash
cp /etc/miofile .
echo "Copiato." 

Il programma copierebbe il file /etc/miofile nella directory corrente e 
stamperebbe a video la stringa "Copiato.". Il programma funzionera' ad una
condizione, che esista un file chiamato miofile nella directory /etc,
altrimenti otterremmo dalla shell una risposta del tipo:

root@darkstar:#./test1.sh
cp: /etc/miofile: No such file or directory
"Copiato."

Possiamo osservare che c'e' un problema, non e' sicuro che tutti quelli che 
eseguono questo programma abbiano il file miofile nella directory /etc,
sarebbe meglio se il programma controllasse l'esistenza di /etc/miofile prima
di provare a copiarlo. Scrivendo il procedimento logico in pseudo-codice 
poteremmo dire 

SE /etc/miofile esiste, ALLORA
copia /etc/miofile nella directory corrente
e stampa a video il messaggio "Copiato."
ALTRIMENTI
stampa a video il messaggio "il file non esiste"

Come e' possibile ottenere un risultato come questo in un programma di shell?
Semplice usando le strutture, o i costrutti, di controllo che la bash ci mette
a disposizione, cioe': if, while, until, for e case. Ogni struttura di
controllo necessita di un comando di apertura e uno di chiusura, un po' come i
tag in html. Per esempio la struttura di controllo "if" comincia appunto con
"if" e termina con "fi". Le strutture di controllo sono funzioni incluse nella
shell nativamente, quindi a differenza di quanto fatto finora non facciamo uso
di comandi esterni. Cominciamo ad esaminarle.

+--------------------------Il costrutto if------------------------------------+

if ... else ... elif ...fi
Uno dei costrutti piu' usati e' "if" che ci permette di modificare il flusso
del programma al verificarsi di una condizione. Per usare il costrutto "if" 
dobbiamo usare il comando di test che puo' essere sottinteso dall'uso delle
parentesi, in pratica possiamo riscrivere il codice di prima cosi':

#!/bin/bash
if test -f /etc/miofile
then
	# il file esiste quindi lo copio e stampo il messaggio
        cp /etc/mia_var .
        echo "Copiato."
else
        # il file non esiste quindi stampo il messaggio e esco
        echo "il file non esiste"
        exit
        fi 

Notare che la tabulazione non e' obbligatoria ma rende il codice piu'
leggibile, aiutando a separare i blocchi di istruzioni.
Salviamo il programma con il nome di test2.sh, diamogli i giusti permessi ed
eseguiamolo con il solito ./test2.sh .
La parola chiave test che controlla se il file esiste, puo essere sostituita 
dalle parentesi quadre, facendo attenzione a lasciare uno spazio prima degli
argomenti, notare anche il punto e virgola che separa le istruzioni. Tutto
quello che si trova dopo il punto e virgola viene interpretato come si
trovasse su una riga a se' stante.
Esempio:

if  [ -f /etc/miofile ]; then

I controlli che la shell ci permette di fare su di un file sono i seguenti:

-d controlla se il file e' una directory
-e controlla se il file esiste
-f controlla se il file e' un file regolare
-g controlla se il file ha il bit SGID settato
-r controlla se il file e' leggibile dall'utente che esegue lo script
-s controlla se la dimensione del file non e' 0
-u controlla il file ha il bit SUID settato
-w controlla se il file e' scrivibile

Il costrutto "else" viene usato per indicare al programma l'azione da compiere
se la condizione non e' soddisfatta. C'e' anche il costrutto "elif" che e' la
somma dei costrutti "else" e "if" che permette di evitare un susseguirsi di
"if" quando si vogliono testare piu' condizioni. 
Quando si vuole testare una variabile e' buona norma includerla dentro doppie
virgolette, affinche' venga espansa correttamente. Esempio

if [ "$mia_variabile" -eq 5 ]; then 



IV° PARTE 

+-------------------------Il ciclo while-------------------------------------+

while ... do ... done
Il costrutto "while" viene definito un costrutto ciclico. Fondamentalmente il
suo significato in inglese ci descrive cosa fa', infatti il significato e'
"mentre". Potremmo semplificare in pseudo-codice dicendo:

MENTRE la condizione da testare e' vera
FAI un compito
FINO A CHE la condizione di partenza non e' piu' vera.

Esempio:

#!/bin/bash
x=0;     # assegnoall variabile x il valore 0
while [ "$x" -le 10 ]; do
	echo "Il valore di x: $x"
        # incremento ad ogni ciclo il valore di x di una unita'
        x=$(expr $x + 1)
done 

Possiamo vedere che abbiamo usato il comando test nella sua forma contratta,
con le parentesi quadre per controllare lo stato della variabile x e sapere
se era minore o uguale a 10, e mentre questo valore aumentava in virtu' dei
comandi della linea 6, finche' non si raggiuge il valore 10, viene stampato
il valore attuale. Come per i files ci sono test predefiniti per i numeri:

x -eq y   Vero se x e' equivalente a  y
x -ne y   Vero se x non e' equivalente y
x -gt y   Vero se x e' maggiore y
x -lt y   Vero se x minore y
x -ge y   Vero se e' maggiore o equivalente 
x -le y   Vero se minore o equivalente
Mentre per confrontare le stringhe di caratteri abbiamo:
x = y    Vero se x e' uguale y
x != y   Vero se x non e' uguale a y
-n x     Vero se x non e' nullo o vuoto
-z x     Vero se x e' nullo o vuoto

Riprendendo l'esempio appena fatto un altro dubbio vi potrebbe venire
osservando la riga

x=$(expr $x + 1)

abbiamo detto che incrementa di un unita' la variabile x, ma dobbiamo chiarire
cosa significa $(...) . Questo e' un modo per dire all'interprete della shell
che vogliamo sia eseguito il comando "expr", e vogliamo che il risultato sia
assegnato alla variabile x. Ogni comando tra le parentesi verra' eseguito.
Esempio:

#!/bin/bash
me=$(whoami)
echo "io sono $me."

Provatelo e capirete meglio, potremmo anche riscriverlo in forma breve come:

#!/bin/bash
echo "io sono $(whoami)."

In seguito vedremo un'altro metodo per assegnare ad una variabile il risultato
di un comando.

+----------------------------Il ciclo until-----------------------------------+

until ... do ... done
Fondamentalmente simile al precedente invece che "mentre" il costrutto
ciclera' "fino a che" la condizione e' vera. Mettete in esecuzione l'esempio
e capirete la sottile differenza osservando il numero dei cicli.

#!/bin/bash
x=0
until [ "$x" -ge 10 ]; then
	echo "Current value of x: $x"
        x=$(expr $x + 1)
done 

+----------------------------Il costrutto for--------------------------------+

for ... in ... do ... done
Il costrutto for e' usato fondamentalmente per ripetere un dato comando un
certo numero di volte. Per esempio volendo stampare dieci punti potremmo
scrivere un file di shell cosi':

#!/bin/bash
echo -n "Incomincio a stampare "
for mia_var in 1 2 3 4 5 6 7 8 9 10; do
	echo -n "."
        echo " finito."
done 

L'opzione -n passata al comando "echo" prima dell'argomento fa si' che il
comando	non emetta il carattere di nuova linea (/n o newline) che farebbe
andare a capo. La variabile mia_var conterra' di volta in volta i valori da
1 a 10, stampando un punto per ogni valore. Per chiarire possiamo provare un
altro script:

#!/bin/bash
for x in pippo pluto pierluigi; do
	echo "Il valore della variabile x : $x"
done 

Vedrete che verra' stampato di volta in volta il valore della variabile.
Per fare un'esempio piu' utile aggiungeremo il suffisso .txt a tutti i file
nella directory corrente.

#!/bin/bash
for file_presenti in *; do
	echo "Aggiungo .txt al file $file_presenti..."
	mv $file_presenti $file_presenti.txt
done

Il carattere "*" e' un carattere jolly che si espande in "tutto nella
directory corrente", il comando "mv" e' usato per rinominare il file.

+--------------------------Il costrutto case---------------------------------+

case ... in ... done
Il costrutto "case" e' molto simile al costrutto "if", fondamentalmente viene
usato per testare una condizione contro valori noti senza usare svariati "if".
Un esempio chiarira':

#!/bin/bash
x=5     # assegno a x il valore  5
# incomincio a testare x
case $x in
	0) echo "Il valore di x: 0."
   	   ;;
	5) echo "Il valore di x: 5."
   	   ;;
	9) echo "Il valore di x: 9."
   	   ;;
	*) echo "Valore sconosciuto"
esac 

Il costrutto "case" testera' il valore di x contro tre valori noti, prima 
controllera' se il valore e' 0, successivamente se e' 5, al termine se il
valore e' 9. Se nessuna delle condizioni si avverera' verra' eseguita la
il blocco di codice della condizione "*" che abbiamo visto essere espansa
come "qualsiasi valore". Ogni blocco di codice che segue una condizione deve
essere terminato con la sequenza di caratteri ";;". Usando il ciclo "if"
avremmo dovuto scrivere:

#!/bin/bash
x=5     # assegno a x il valore  5
if [ "$x" -eq 0 ]; then
	echo "Il valore di x: 0."
elif [ "$x" -eq 5 ]; then
        echo "Il valore di x: 5."
elif [ "$x" -eq 9 ]; then
        echo "Il valore di x: 9."
else
	echo "Valore sconosciuto"
fi 

Il costrutto e' sicuramente meno leggibile.

+----------------------------Le virgolette-------------------------------------+

Le virgolette sono molto importante nello shell scripting, ci sono tre tipi di
virgolette ed ognuno ha un significato diverso per l'interprete della shell.

"	virgoletta doppia
'	virgoletta semplice
`	virgoletta invertita

Le virgolette DOPPIE sono usate spesso per impostare un valore stringa e per
preservare gli spazi nelle stringhe.
Esempio:

x="stringa con quattro parole"

la variabile conservera' la stringa come un valore singolo, cioe' una stringa
formata da caratteri e da spazi, in sintesi un solo argomento. Applichiamo
quanto detto ai comandi, se noi digitassimo:

mkdir ciao mondo
ls -F

avremmo:

ciao/ mondo/

cioe' avremmo creato due directory distinte perche' il comando tratterebbe 
quello che lo segue come due argomenti diversi. Ma se invece digitiamo:

mkdir "ciao mondo"
ls -F

avremmo:

ciao/ ciao mondo/ mondo/

cioe' il comando interpreterebbe la stringa come un unico argomento.
(ciao/ e mondo/ sono le directory create col comando precedente).
Usate con le variabili ne espandono il valore contenuto.
Le virgolette SEMPLICI si usano quasi sempre con le variabili, infatti una
variabile inclusa in virgolette semplici, al contrario delle doppie, non
verra' espansa nel corrispondente valore. Un esempio:

#!/bin/bash
x=5     
# uso le doppie virgolette
echo "Usando le doppie virgolette x : $x"
# uso le virgolette semplici 
echo 'Usando le virgolette semplici x : $x'

Si ottine:
Usando le doppie virgolette x : 5
Usando le virgolette semplici x : $x

Anche le virgolette semplici fanno si' che i termini inclusi vengano
considerati un argomento singolo.

Le virgolette ROVESCIATE sono completamente diverse dalle precedenti due.
Ricordate uno degli esempi precedenti quando scrivemmo :

x=$(expr $x + 1) 

grazie alle virgolette rovesciate possiamo riscriverlo cosi':

x=`expr $x + 1` 

in pratica come avevamo gia' visto assegnamo il risultato di un comando o di
una pipe di comandi ad una variabile.

+------------------------Le operazioni con i numeri--------------------------+

La shell Bash ci permette, come abbiamo gia' visto, di eseguire espressioni
aritmetiche. Si possono effettuare operazioni in due modi, uno l'abbiamo gia'
incontrato ed e':

x=`expr $x + 1` 

l'altro è inserire l'espressione da valutare tra doppie parentesi, attenzione
doppie non singole come nell'esempio di prima, e cioe'

x=$(($x+1))

possiamo verificare con

#!/bin/bash
x=8     
y=4     
z=$(($x + $y))
echo "La somma di $x + $y = $z"

Gli operatori aritmetici messi a disposizione sono:

Addizione		+
Sottrazione		-
Moltiplicazione	*
Divisione		/
Resto o Modulo	%

Sono le principali operazioni, tranne resto o modulo che restiuisce appunto il
resto di una divisione.
Esempio:

#!/bin/bash
x=5   
y=3   
somma=$(($x + $y))   
sottrazione=$(($x - $y))   
moltiplicazione=$(($x * $y)) 
divisione=$(($x / $y)) 
modulo=$(($x % $y)) 
echo "Somma: $somma"
echo "Sottrazione: $sottrazione"
echo "Moltiplicazione: $moltiplicazione"
echo "Divisione: $divisione"
echo "Modulo: $modulo"

Passiamo oltre.

--------------------------------------------------------------------------------

V° parte 

+---------------------------Input dall'utente--------------------------------+

Vedremo ora come leggere i valori immessi dall'utente per usarli nei nostri
programmi, useremo per questo il comando "read" che e' un comando interno
della shell che copiera' l'input dell'utente in una variabile. Esempio:

#!/bin/bash
# programma che prende l'input dall'utente e lo saluta
echo -n "Inserisci il tuo nome: "
read nome_utente
echo "Ciao $nome_utente !"

Il comando "read" apettera' che l'utente immetta qualcosa e prema ENTER o
INVIO, e aspettera' all'infinito che venga premuto il tasto INVIO. Se l'utente
non immette nulla e preme INVIO read sara' eseguito ugualmente e il programma
continuera' con l'istruzione successiva. Cominciamo a controllare dunque che 
l'utente immetta qualcosa:

#!/bin/bash
# programma che prende l'input dall'utente e lo saluta

echo -n "Inserisci il tuo nome: "
read nome_utente

#controlliamo se l'utente ha immesso qualcosa

if [ -z "$nome_utente" ]; then
    	echo "Non hai digitato nulla!"
	exit
fi

echo "Hello $nome_utente !" 

+------------------------------Le funzioni-----------------------------------+

Le funzioni rendono la programazione meno ripetitiva e piu semplice, ci
permettono di suddividere il programma che dobbiamo scrivere in tanti piccoli
sottoprogrammi, per essere stringati una funzione e' un pezzo di codice che
accetta o no valori in entrata, compie delle azioni, e puo' emettere dei
risultati siano essi semplici valori booleni di ritorno o dati piu' complessi.
Esempio di funzione semplice:

#!/bin/bash
#dichiariamo la  funzione ciao() 
ciao()
	{
	echo "Sono nella funzione ciao()"
        } 
echo "Sto per chiamare la funzione ciao()..."
# chiamata della funzione ciao() 
ciao
echo "Sono uscito dalla funzione ciao()" 

Mandando in esecuzione questo pezzo di codice, ci accorgiamo che l'unica cosa
che fa' e' stampare un messaggio, ovviamente le funzioni possono fare cosei
molto piu' complesse, ma questo ci serve per osservarne la struttura. La
stampa a video del messaggio contenuto nella dichiarazione di funzione e'
avvenuta quando abbiamo richiamato la funzione, dopo averla precedentemente
dichiarata. Infatti la bash quando incontra la linea che richiama la funzione
ciao va' a ritroso, perche' per usare una funzione bisogna averla dichiarata
in precedenza, cercando il nome funzione seguito da due parentesi tonde oppure
la parola chiave function che noi non abbiamo usato perche' facoltativa.
Volendo avremmo potuto scrivere:

function ciao()
	{
	echo "Sono nella funzione ciao()"
        } 

La dichiarazione di funzioni quindi consta della parola chiave "function" che
e' facoltativa, del nome che assegniamo alla funzione seguito da apertura di
parentesi tonda, e successiva chiusura, per leggibilita' un ritorno a capo, 
una aperture di parentesi graffe, il blocco di codice che la compone, la 
chiusura delle parentesi graffe.

function nome_funzione()
	{                            #inizio blocco che verra' eseguito
	istruzioni
	}		             #fine blocco che verra' eseguito 
		
Tutto quello che si trova tra le parentesi graffe, dichiarazione di variabili,
espressioni complesse, ecc. si definisce di ambito locale, cioe' i valori sono
definiti e hanno un senso solo all'interno di essa. Se chiamiamo una funzione
prima di averla definita otterremmo in risposta un messaggio di errore
"command not found", quindi e' buona norma mettere le dichiarazioni di
funzione raggruppate in testa al nostro script. Facciamo un esempio piu'
completo:

#!/bin/bash
# admin.sh un tool stupidino
# function nuovo_utente() la funzione che crea un nuovo utente 
# la dichiariamo in cima allo script prima della chiamata
nuovo_utente()
	{
        echo "Stai per aggiungere un nuovo utente..."
        sleep 2	    #semplicemente sospende per due secondi l'esecuzione	
        adduser     #manda in esecuzione il comando di shell "adduser" 
        } 
#iniziamo lo script
echo "1. Aggiungi un utente"
echo "2. Esci" 
echo "Digita la scelta: "
read scelta
case $scelta in
1) nuovo_utente     #se il confronto e' positivo chiamiamo nuovo_utente
   ;;
*) exit		    #in tutti gli altri casi terminiamo col predefinito
  ;;
esac 

Per eseguire questo script bisogna entrare nel sistema con l'account di root, 
essendo il comando "adduser" eseguibile solo da root.

+-----------------------Intercettare i segnali di sistema---------------------+

L'argomento e' un po' complesso ma lo accenniamo. E' possibile grazie al
comando di shell "trap" intercettare i segnali si sistema. Se noi durante 
l'esecuzione di uno script o di un comando premiamo la combinazione di tasti 
Ctrl+c, mandiamo allo script un segnale di interrupt che fara' terminare
l'esecuzione. Possiamo intercettare le principali chiamate e far continuare 
l'esecuzione del nostro programma, usando appunto il comando "trap", per
conoscere i segnali che puo' gestire digitate trap -l, la sua sintassi e':
trap azione segnale
L'azione e' tutto cio' che vogliamo accada se il segnale di interrupt viene
chiamato. I segnali possono essere indicati sia in forma numerica, sia in
forma letterale, nel secondo caso si devono omettere le prime tre lettere, 
generalmente "SIG", per esempio se volessimo intercettare il segnale di 
interrupt "SIGINT" (codice numerico 2) dovremmo usare la stringa "INT".
Esempio:

#!/bin/bash
# usiamo il comando trap 
# catturiamo la sequenza di tasti Ctrl-c ed eseguiamo la funzione spiacente
trap spiacente INT 
# la funzione spiacente stampa semplicemente un messaggio
spiacente()
	{
        echo "Spiacente, non puoi farlo"
        sleep 3
        } 
# contiamo alla rovescia da 10 a 1 
for i in 10 9 8 7 6 5 4 3 2 1; do
	echo "mancano $i secondi "
        sleep 1
done
echo "...finito"

Adesso salviamo, rendiamo eseguibile, avviamolo, e mentre lo script conta
proviamo a premere Ctrl+c, normalmente l'esecuzione si sarebbe interrotta, ma
in questo caso il segnale di interrupt e' stato intercettato da "trap" che
invece di uscire ha richiamato la funzione spiacente(). Se si vuole
semplicemente far ignorare al programma il segnale di interrupt basta
passargli al posto dell'azione due virgolette semplici vuote " '' " , se
vogliamo annullare l'effetto del comando "trap" dobbiamo resettarlo
passandogli un trattino " - ".

trap '' INT
trap - INT

Il reset riportera' la sequenza di tasti alla sua normale funzione.

+--------------------------I costrutti AND e OR-------------------------------+

Abbiamo gia' parlato delle strutture di controllo ("if...elif..ecc"), ma
abbiamo un paio di cosette da aggiungere: le direttive AND, generalmente
indicata con la sequenza di caratteri "&&", e la direttiva OR, indicata con
"||" (doppio pipe il carattere sopra "\").
La sintassi della direttiva AND e':
prima_condizione && seconda_condizione
La direttiva AND controlla prima la condizione a sinistra, se restituisce vero,
controlla quella di destra, se ambedue sono soddisfatte continua con
l'esecuzione del codice successivo. In pseudo-codice potremmo scrivere:
SE la prima condizione e' vera E la seconda condizione e' vera ALLORA...
Esempio:

#!/bin/bash
x=5
y=10
if [ "$x" -eq 5 ] && [ "$y" -eq 10 ]; then
	echo "Entrambe le condizioni sono vere"
else
        echo "Le condizioni non sono vere"
fi 

Cambiate il valore di x in 12 e vedete cosa succede.
Il costrutto OR non si comporta molto differentemente, testa se la prima
condizione e se e' falsa passa alla seconda, se almeno questa e' vera esegue
i comandi, altrimenti passa ai comandi successivi. In pseudo-codice:
SE la prima condizione e' vera OPPURE la seconda condizione e' vera ALLORA...
Esempio:

#!/bin/bash
x=3
y=2
if [ "$x" -eq 5 ] || [ "$y" -eq 2 ]; then
	echo "Una delle condizioni e' vera"
else
        echo "Nessuna delle condizioni e' vera"
fi 

Questi costrutti servono spesso per evitare l'annidarsi di piu' istruzioni
"if", anche se resta possibile farlo, il primo esempio sarebbe stato:

#!/bin/bash
x=5
y=10
if [ "$x" -eq 5 ]; then
	if [ "$y" -eq 10 ]; then
        	echo "Entrambe le condizioni sono vere"
        else
                echo "Nessuna condizione e' vera"
        fi
fi 

+-----------------------------Gli argomenti----------------------------------+

Avrete notato che la maggior parte dei comandi Linux non si limita ad eseguire
un compito in maniera autonoma, ma spesso richiede delle informazioni extra
che l'utente deve fornirgli, se la chiamata del comando e' incompleta ci
appare spesso il messaggio che ci suggerisce l'uso appropriato.
Il comando "more" che serve a visualizzare un file, se digitato da solo 
produrra' un messaggio che ci informa sul suo uso. Il comando si aspetta degli 
argomenti, cioe' dei valori immessi dall'utente. Ora dobbiamo sapere che la 
shell utilizza delle variabili speciali per tenere in memoria questi valori.
Queste sono le variabili speciali utilizzate:

$#	Contiene il numero dei parametri passati
$n	 (n è un numero, es $1 oppure $99)
	Contiene il valore singolo dei parametri ordinati per immissione (1-9)
$?	Contiene il valore di uscita dell'ultimo comando 
$0	Contiene il nome del programma
$*	Contiene tutti i parametri ordinati per immissione ($1 $2 ...).
"$@"	Contiene tutti i parametri ordinati per immissione inclusi in doppie
	virgolette ("$1" "$2" ...).

Esempio:

#!/bin/bash
# script per visualizzare il primo argomento
# prima controlliamo se ci sono argomenti
if [ "$#" -ne 1 ]; then
	echo "uso: $0 argomento "
fi 
echo "L'argomento pasato e' $1"

Chiamatelo prima senza argomento e poi facendo seguire un numero o una stringa
al comando e verificate cosa succede.

+---------------------------Le redirezioni e le pipe-------------------------+

Normalmente quando eseguiamo un comando il suo risultato viene stampato a
video. Per esempio il comando
echo "Ciao mondo"
stampa la stringa "Ciao mondo", perche' lo 'standard output' del programma e'
la consolle. Ci sono tre canali predefiniti per i comandi in Linux, lo
'standard input', lo 'standard output' e lo 'standard error', spesso il secondo
e il terzo coincidono per permetterci di vedere gli errori generati, ma
altrettanto spesso gli errori, e quindi lo 'standard error, vengono rediretti su
di un file di log. La redirezione ci permette di modificare i comportamenti
standard del programma, i suoi operatori sono:

>	redirige lo standard output su di un file sovrascrivendo i dati
>>	redirige lo standard output su di un file appendendo alla fine i dati

Esempio

echo "Ciao mondo"> mio.file
cat mio.file

stampera':

"Ciao mondo"

echo "mi appendero' al file" >> mio.file
cat mio.file

stampera':

"Ciao mondo"
"mi appendero' al file" 

Le pipe permettono di passare a un comando come argomento il risultato di un
altro comando, possiamo cosi' concatenare piu' comandi per ottenere quello che
vogliamo. Per iniziare concateneremo il risultato del comando "cat" (che tra
le altre funzioni e' utile per leggere un file) al comando grep, che ci
restituisce le righe di un file che corrispondono ad un criterio di ricerca.
Se volessimo trovare nel file /etc/passwd la riga che si riferisce all'utente
"pluigi" presente nel mio sistema potremmo digitare:

cat /etc/passwd | grep pluigi

il risultato sarebbe

pluigi:x:1002:100:users,,,:/home/pluigi:/bin/bash 

Il comando cat "passa" il contenuto del file al comando "grep" che esegue la
ricerca e ci restituisce il risultato sullo standard output.
Possiamo concatenare pipe e redirezioni:

cat /etc/passwd | grep pluigi > mio.file

non apparira' nulla perche' lo indirizziamo sul file, leggiamo il file

cat mio.file

il risultato:

pluigi:x:1002:100:users,,,:/home/pluigi:/bin/bash 

+------------------------------Files temporanei-------------------------------+

Spesso ci servira' creare dei file temporanei su cu scrivere dei valori, o
conservarli tra una chiamata ed un'altra dello script. La cosa importante da
tenere a mente e' che no possiamo rischiare di sovrascrivere file esistenti.
Per fare questo ci serviamo della sequenza di caratteri "$$" che possiamo
usare come prefisso o postfisso al nome del file.
Esempio:
creiamo un file

touch mio_file

controlliamo

ls

risultato:

mio_file

usiamo il postfisso

touch mio_file.$$

controlliamo

ls

risultato:

mio_file    mio_file.689

+---------------------------Valori di ritorno--------------------------------+

Molti programmi restiuiscono valori di ritorno che ci informano sul risultato
della loro esecuzione, se consultiamo la pagina man di "grep" leggeremo che il
programma restituisce uno 0 se non riesce a trovare nulla, altrimenti un 1.
Il perche' dovremmo interessarci del valore di uscita di un programma ci sara'
chiaro con un esempio. Se noi volessimo sapere se un utente esiste nel sistema
potremmo controlare con grep se e' presente nel file /etc/passwd :

grep "marcellino" /etc/passwd

ora dato che "marcellino" non e' un utente della mia macchina il risultato
sara' un prompt vuoto. Sarebbe opportuno avere un messaggio che ci avvisi che
non e' stato trovato nulla, quindi useremo la variabile speciale "$?" che 
contiene il valore di uscita del comando eseguito:

#!/bin/bash
# greppiamo per cercare l'utente 
#e redirigiamo l'uscita su /dev/null un file che e' paragonabile a un cestino
#in modo che non appaia nulla
grep "marcellino" /etc/passwd &> /dev/null
# controlliamo il suo valore di ritorno
if [ "$?" -eq 0 ]; then
	echo "Utente trovato"
        exit
else
	echo "Utente non trovato"
fi   
     
Questo e' valido per i comandi della shell, ma come facciamo ad avere dei
valori di ritorno dai nostri script? Semplice il comando exit accetta un
argomento, che puo' essere un numero da restituire. Normalmente 1 significa
che c'e' stato un errore, 0 che tutto e' andato bene.

Esempio:
#!/bin/bash
if [ -f "/etc/passwd" ]; then
	echo "Il file di password esiste"
        exit 0
else
	echo "Il file di password non esiste"
        exit 1
fi 

Usando i valori di ritorno possiamo passare dei valori ad altri script, che si
comporteranno di conseguenza.
Anche le funzioni possono avere dei valori di ritorno, grazie al comando
"return", che accetta un argomento numerico, il suo uso e' simile al comando
"exit" visto in precedenza. 
Esempio:

controlla_passwd()
	{
		if [ -f "/etc/passwd" ]; then
                   echo "Il file di password esiste"
                     #faccio restituire il valore 0
                   return 0
               else
                    #faccio restituire il valore 1
                   echo "Il file di password non esiste"
                   return 1
               fi
         } 
# metto in una variabile il valore di uscita
mia_var=controlla_passwd
#controlliamo il valore testando il contenuto della vaiabile 
if [ "$mia_var" -eq 0 ]; then
	echo "Il file esiste"
        exit 0
else
        echo "Il file non esiste"
        exit 1
 fi 

+----------------------------Le conclusioni----------------------------------+

Quanto detto illustra solo la minima parte delle potenzialita' dello shell
scripting, se volete approfondire c'e' l'ottimo 
"Advance-Bash-Scripting-How-To"
e molti altri testi disponibili...sta a voi.
Segnalate errori e baggianate a pierluigi.previtali@tiscali.it
Sentitevi liberi di scrivermi, e io mi sentiro' libero di non rispondere.
Questo documento e' coperto da licenza GPL .
Interamente scritto con VIM .

 
 
 
 



Maggiori informazioni sulla lista linuxludus