Guida al bash-scripting

Andare in basso

Guida al bash-scripting

Messaggio Da floatman il Mer Set 16, 2009 12:34 pm

Spesso si dichiara la potenza della console di Linux; molto più spesso chi lo dice parla per sentito dire e non ne capisce il motivo.
Qualunque funzione del sistema può essere eseguita da console, dalla creazione di file, alla gestione del kernel, il download, la compilazione di sorgenti ecc.
Insomma, Linux *è* la console, tutto il resto è una semplice interfaccia per la comodità dell'utente (e far bella figura con gli amici).

Il bash-scripting è il linguaggio di gestione dei comandi di Linux, cioè uno script gestisce una catena di comandi tramite le funzioni tipiche dei linguaggi di programmazione.

In questa guida analizzeremo proprio il bash-scripting in sè, omettendo la parte riguardante tutti i comandi di Linux e di ogni applicazione utilizzata negli script stessi.

SCRIVERE SCRIPT IN BASH

#!/bin/bash

Questa qui sopra è l'intestazione di uno script che dice a Linux quale applicazione eseguirà lo script stesso, cioè la console /bin/bash
(notare la somiglianza con altri linguaggi nati da Bash...)
Dopo aver scritto il nostro script potremo salvarlo con estensione .sh oppure senza estensione; rendendo eseguibile lo script, ed eventualmente posizionandolo in qualche path di sistema, sarà possibile richiamarlo semplicemente scrivendo in console il nome del file.
Per ora prendetelo per buono, vedremo tutto più avanti...

HELLO WORLD

Iniziamo con il solito programma: aprite l'editor di testo e scrivete
Codice:
#!/bin/bash
# il mio primo script
echo "Hello World!"
il carattere "#" in seconda riga indica un commento (e non verrà visualizzato).
Potrete salvare il file come "pippo.sh" oppure come "pippo" nella vostra /home, per eseguirlo sarà sufficiente scrivere a console "bash pippo" oppure "bash pippo.sh".

Possiamo anche rendere eseguibile lo script tramite il comando:

$ chmod 755 pippo

(755 lettura, scrittura, esecuzione al proprietario; lettura e esecuzione a gruppo e altri)
in questo modo il nostro script sarà eseguibile con questo comando:

$ ./pippo

Ora lo script risulta eseguibile; che succede se lo inseriamo in un path di sistema? I path di sistema sono visualizzabili tramite il comando "echo $PATH", che restituirà ad esempio il valore /usr/local/bin. Vediamo un po' che succede:

# cp pippo /usr/local/bin

dopo la copiatura come eseguiremo pippo?

$ pippo

e otterremo la nostra stringa "Hello World!". Come vedete abbiamo creato un comando di Linux!
Certo direte voi, bella forza...vero, andiamo un po' più a fondo:

VARIABILI E FUNZIONI

Una variabile è una locazione di memoria riservata ad un certo dato; bash utilizza fondamentalmente due sistemi di assegnazione: o il dato viene dichiarato direttamente, oppure viene memorizzato tramite richiesta all'utente.
L'assegnazione di variabile avviene senza alcun simbolo particolare, per venire richiamata il nome andrà preceduto dal carattere "$":
Codice:
#!/bin/bash
STRINGA="Hello World!"
echo "Chi ha scritto questo script inutile?"
read AUTORE
echo "Questo script scemo che scrive \"$STRINGA\" e' scritto da $AUTORE..."
All'esecuzione:

flotaman@debian:~$ bash prova_bash
Chi ha scritto questo script inutile?
floatman
Questo script scemo che scrive "Hello World!" e' scritto da floatman...
flotaman@debian:~$


Come vedete "STRINGA" è una variabile inserita da noi, mentre "AUTORE" è una seconda variabile che viene richiesta all'utente.
Il simbolo "\" mi permette di scrivere le virgolette, che altrimenti verrebbero ignorate.
L'utilizzo delle lettere maiuscole per le variabili è una prassi ma non è necessaria, ovviamente per UNIX dopo aver scritto "STRINGA" non sarà possibile richiamarla come "$stringa" ma solo utilizzando lettere maiuscole. In questo caso ho utilizzato due stringhe di testo, ovviamente è possibile utilizzare anche numeri o comandi Linux in maniera da gestirne l'output...ma questa è un'altra storia.

CONTROLLO DI FLUSSO

Il controllo di flusso comprende l'insieme delle funzioni logiche utilizzate da un script.
Per dichiarare una funzione utilizziamo questa dicitura:

function nome_funzione
{
argomenti...

}


Utilizzare una funzione ci permette di richiamarla più volte nel corso di uno stesso script.
L'argomento può comprendere sia elementi "echo", sia ovviamente strumenti di controllo come le classiche funzioni dei vari linguaggi.

IF

La funzione IF in bash assume tre costrutti:

# comandi eseguiti se condizione è vera

if condizione ; then
comandi
fi

# comandi_1 eseguiti se la condizione è vera, oppure comandi_2 se è falsa

if condizione ; then
comandi_1
else
comandi_2
fi

# comandi_1 se condizione_1 è vera, comandi_2 se condizione_1 è falsa *e condizione_2 è vera*

if condizione_1 ; then
comandi_1
elif condizione_2
comandi_2
fi


Nelle condizioni possono essere inseriti i veri operatori matematici (+, - ecc.) e logici (=, != ecc.)
Un esempio semplicissimo e utilissimo, da imparare a memoria, è il seguente:
Ogni utente ha un id identificativo univoco; per sapere l'id del vostro utente basta dare da terminale il comando "id -u" che fornisce l'id dell'utente corrente.
Se vi chiedessi qual'è l'id di root cosa rispondereste? Ovviamente sarà "0" (non stavate pensando "1" vero?)
Allora guardate un po' se vi piace questo:
Codice:
#!/bin/bash

STRINGA="Hello World!"

function verifica_root
{
if [ $(id -u) != "0" ]; then
  echo "Devi essere root anche per scrivere \"$STRINGA\" !"
  echo "Benvenuto su Linux..."
  exit 0
fi
}

function hello_world
{
echo "Hello World!"
}

verifica_root
hello_world
exit 0
Come potete vedere, nella sua stupidità stiamo costruendo qualcosa...

All'inizio ho posto le variabili esterne alla funzione, poi "verifica_root" va a vedere se ho i permessi di root per eseguire lo script.
Le parentesi [] delimitano la condizione che è appunto "id utente diverso da 0"; qualora lo script non venga eseguito come root otteremo il messaggio di echo della funzione; "exit 0" ci fa tornare alla console per eseguire nuovamente lo script.

Solo le ultime tre righe sono il flusso vero e proprio del programma.

In questo caso abbiamo utilizzato il valore "!=" in quanto ben conosciuto, a complicarci la vita esistono altri parametri che qui non verranno spiegati per intero.

Una delle cose più potenti di bash per regolare le funzioni condizionali è il "test". Questo gestisce le condizioni del flusso sia in base agli operatori logici e matematici, sia in base a particolari opzioni tipiche di bash:

Un esempio tipico di utilizzo è rappresentato dal file .bash_profile esistente nella /home di ogni utente, tale file regola le opzioni utente di esecuzione di bash. Aprendo .bash_profile troverete questo:
Codice:
# prima regola
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
# seconda regola
if [ -d ~/bin ] ; then
    PATH=~/bin:"${PATH}"
fi
Prima regola: se esiste il file (-f) /home/user/.bashrc, utilizza quel file
Seconda regola: se esiste la directory (-d) /home/user/bin, inseriscila tra i path di sistema.

Vediamo qualche altro test importante oltre -f e -d (lista completa con il comando "help test"...buona fortuna):

[-r, -w, -x] nome_file => vero se nome_file è [leggibile, scrivibile, eseguibile] da parte dell'utente.
Nelle applicazioni grafiche gestisce alert box e salvataggio file

file_1 [-nt, -ot] file_2 => vero se file_1 è [più nuovo, più vecchio] di file_2
La base dei gestori aggiornamenti (apt-get, yum, emerge...)

[-z, -n] stringa => vero se stringa è [zero (=vuota), non zero]
Gestisce valori nulli o informazioni supplementari

Qualcuno avrà notato che ancora non ho parlato di operazioni matematiche...ora possiamo farlo per scrivere un utile esempio.
Iniziamo dalle basi, ritornando su qualcosa visto in precedenza e aggiungiamo qualcosa di nuovo e utile:
Codice:
#!bin/bash

# inserisco le variabili

CIFRA_1=4

# inserisco le funzioni

function digita_presto
{
echo "inserisci velocemente il secondo addendo da sommare a $CIFRA_1:"
if read -t 4 CIFRA_2; then
    echo "Bravo! Hai fatto presto a scrivere $CIFRA_2"
else
    echo "Ho scritto \"inserisci velocemente\" oppure no?"
exit 0
fi
}

function operazioni_numeri
{
echo "$CIFRA_1 + $CIFRA_2 = $((CIFRA_1 + CIFRA_2))"
echo "$CIFRA_1 - $CIFRA_2 = $((CIFRA_1 - CIFRA_2))"
echo "$CIFRA_1 * $CIFRA_2 = $((CIFRA_1 * CIFRA_2))"
echo "$CIFRA_1 : $CIFRA_2 = $((CIFRA_1 / CIFRA_2))"
echo "Modulo di $CIFRA_1 : $CIFRA_2 = $((CIFRA_1 % CIFRA_2))"
}

function pari_dispari
{
if [ $((CIFRA_2 % 2)) -eq 0 ]; then
    echo "Il numero inserito e' pari"
else
    echo "Il numero inserito e' dispari"
fi
}

# flusso vero e proprio del programma

digita_presto
operazioni_numeri
pari_dispari
exit 0
Uh...quante cose nuove!
Come si vede read può prendere delle opzioni, "-t num_secondi" imposta un tempo in secondi di prosecuzione dello script qualora non venga inserito alcun valore. Un'altra importante opzione è -s che impadisce la visualizzazione dei caratteri digitati, ad esempio password.

L'utilizzo degli operatori matematici credo non richieda alcun commento esclusa la gestione del modulo, cioè il resto di una divisione.
"if [ $((CIFRA_2 % 2)) -eq 0 ]" quindi "se il resto di (CIFRA_2 : 2) è uguale a 0" la funzione risponde vero e il numero è pari, altrimenti è dispari.
Alla fine della funzione, lo script esce a console con "exit 0", se questo fosse omesso il programma continuerebbe e produrrebbe un errore.
Da notare come i valori inseriti all'interno delle operazioni matematiche non utilizzino il simbolo "$", che viene invece riportato per indicare l'operazione stessa.

Per concludere possiamo dire che if supporta ovviamente l'annidamento per gestire più opzioni:
Codice:
#!/bin/bash

echo "Inserisci un numero tra 1 e 3 (estremi inclusi)"
read numero
if [ "$numero" = "1" ]; then
    echo "hai scritto 1"
else
    if [ "$numero" = "2" ]; then
   echo "hai inserito 2"
    else
   if [ "$numero" = "3" ]; then
       echo "hai inserito 3"
   else
       echo "Ho detto un numero tra 1 e 3 !"
   fi
    fi
fi
Come vedete la forma è piuttosto complessa, pensate a programmi con un'enormità di funzioni (ad esempio "wget"), occorreva un comando per semplificarla...

CASE

Subito al sodo:
Codice:
#!/bin/bash

echo "Questo programma mette ordine nella /home."
echo
echo " 1 - ordina tutti i file .mp3 dentro /mp3"
echo " 2 - ordina le foto .jpg dentro /foto"
echo " 3 - mette tutti i .wmv porno dentro /parrocchia"
echo
echo "Scegli cosa ordinare:"
read numero
case $numero in
   1 ) mkdir $HOME/mp3
       mv *.mp3 $HOME/mp3
       ;;
   2 ) mkdir $HOME/foto
       mv *.jpg $HOME/foto
       ;;
   3 ) mkdir $HOME/parrocchia
       mv *.wmv $HOME/parrocchia
       echo "mi fai veramente schifo! Che dice tua moglie?"
       ;;
   * ) echo "Ma possibile che su *tre* numeri riesci a sbagliare?"
esac
exit 0
;;
Il codice ormai dovrebbe essere facilmente comprensibile (giusto?), vediamo cosa c'è di nuovo:
$HOME è un path speciale che indica la /home dell'utente (avremmo anche pututo scrivere /home/$USER/mp3)
Il simbolo "*" indica ovviamente tutti i file di quell'estensione, nell'ultimo caso indica "tutti gli altri valori di $numero"
Da notare l'uso di ";;" in tutti i casi escluso quello di uscita, oltre a "esac" a fine funzione.

Un uso interessante e utile di case è l'opzione di scelta yes/no che ritroviamo in molti programmi per Linux:
Codice:
#!/bin/bash

echo "Questo programma nasconde a tua moglie tutti i tuoi pornetti .wmv"
echo "tutti i file saranno spostati nella directory /parrocchia"
echo -n "Vuoi proseguire (lurido pornomane)? [S/n]: "
read risp_segaiolo
case $risp_segaiolo in
   s | S ) mkdir $HOME/parrocchia
      mv *.wmv $HOME/parrocchia
      echo "mi fai veramente schifo! Che dice tua moglie?"
      ;;
   n | N ) echo "Meglio! Spero che tua moglie ti inXXXi!"
      ;;
   * )   echo "Hey, Pippa-man! S oppure N, o sei gia' diventato cieco?"
esac
exit 0
;;
L'opzione -n di echo (opzioni ovunque...) dice di non andare a capo a fine riga ed infatti la risposta risulterà scritta dopo ":"
Il simbolo "|" inplica un OR, cioè raggruppa le due opzioni per evitare errori di capitalizzazione delle lettere usate.

LOOPING

Per chi non lo sapesse, il looping è l'esecuzione ripetuta di una serie di istruzioni in base a determinate condizioni.
Bash gestisce il looping tramite tre istruzioni: "while", "until", "for"

Le prime due le considereremo insieme perchè rappresentano le due facce della stessa medaglia:
Codice:
#!/bin/bash

# looping con WHILE

numero=0

while [ $numero -le 100 ]; do
   echo "$numero"
   numero=$((numero + 1))
done
# ----------------------------------
#!/bin/bash

# looping con UNTIL

numero=0

until [ $numero -gt 100 ]; do
   echo "$numero"
   numero=$((numero + 1))
done
Ricordate le opzioni di test? Ok, -le (less-than-or-equal, minore o uguale) e -ge (greater-than, maggiore di) ci fanno capire la differenza tra while e until. I due programmi mi pare ovvio che scrivano i numero da 0 a 100 (o no?).

Nel primo caso leggiamo "somma 1 a $numero *finchè* è minore o uguale a 100"; cioè prosegue l'azione finchè la condizione risulta vera.

Nel secondo caso avremo "somma 1 al numero *finchè non* è maggiore di 100"; prosegue l'azione finchè la condizione risulta non vera.

Un utilizzo molto interessante di questi looping è la gestione di demoni senza impostare l'avvio automatico, ad esempio il demone di Tor.
Tor e Privoxy sono programmi di navigazione anonima, l'installazione attiva due demoni in avvio automatico dentro /etc/init.d; i comandi che possiamo dare nel caso non si desideri l'avvio automatico sono "start", "restart" e "stop" da terminale (come root) ogni volta che vogliamo utilizzare Tor. Vediamo quindi se possiamo semplificarci la vita:
Codice:
#!/bin/bash

opzione=
until [ "$opzione" = "x | X" ]; do
echo "GESTIONE TOR/PRIVOXY"
echo
echo "S - Avvia Tor"
echo "C - Chiudi Tor"
echo "R - Riavvia Tor"
echo
echo "X - Esci"
echo
echo -n "Opzione: "
read opzione
case $opzione in
   s | S ) /etc/init.d/tor start
      /etc/init.d/privoxy start
      echo "Tor avviato..."
      echo
      ;;
   c | C ) /etc/init.d/tor stop
      /etc/init.d/privoxy stop
      echo "Tor disattivato"
      echo
      ;;
   r | R ) /etc/init.d/tor restart
      /etc/init.d/privoxy restart
      echo "Tor riavviato"
      echo
      ;;
   x | X ) exit 0
      ;;
   * ) echo "Inserisci S, C o X..."
       echo
esac
done
exit 0
;;
Avviando da root lo script, questo ci farà compiere la scelta desiderata e riproporrà lo stesso menu, fino a che non cliccheremo la lettera "x".
Come vedete la variabile "opzione" è settata in un valore nullo, valore che poi si modifica nel corso della procedura.
I più bravi lettori avranno notato che potevamo anche inserire due funzioni vere e proprie: una per il controllo dei permessi di root e una con quest'ultimo esempio.

FOR

Il ciclo for ha la seguente struttura:

for "variabile" in "valori"; do
"comando"
done


La forma non è amichevole, comunque "variabile" sarà un elemento di una serie di "valori"; finchè "variabile" non sarà individuata all'interno di tutti i "valori" verrà eseguito "comando".
Nonostante la mia pessima spiegazione un esempio ci chiarirà meglio le idee...
Il comando "cat" preleva un elemento in input (un file) per elaborarlo in uno script; il comando "wc" permette una serie di conteggi dei valori all'interno dei file, ad esempio l'opzione -c conta i byte e può quindi essere utilizzata per contare i caratteri usati:
Codice:
#!/bin/bash

function scegli_file
{
echo "Inserisci il percorso del file di testo da usare:"
read file_scelto
}

function conta_lettere
{
conteggio=0
for i in $(cat $file_scelto); do
    conteggio=$((conteggio + 1))
    echo "La parola $conteggio ($i) ha $(echo -n $i | wc -c) caratteri"
done
}

scegli_file
conta_lettere
exit 0
Il nostro ciclo for è solo nella funzione "conta_lettere": "conteggio" viene inizializzato a "0", "cat" passa ogni parola a cui viene assegnato il valore della variabile "i" e questa viene aumentata di 1 ogni volta; infatti alla riga seguente l'operatore "|" prende l'output di "echo -n $i" (cioè la parola stessa) e ne conta il numero di lettere.

CONCLUSIONI

Qui si conclude la nostra visita introduttiva dentro Bash; come ho già spiegato la complesità deriva dal notevole numero di funzioni e opzioni presenti nei vari comandi.
Ovviamente ogni programma utilizzabile da console può essere inserito in uno script.

Per finire, qualche consiglio per vivere felici:

Innanzitutto le solite buone regole della programmazione: ordine nel codice, commenti, debugging

Per quanto Bash possa venire assimilato ad un linguaggio semplice per Linux da parte dei neofiti, fate sempre molta attenzione.
Con bash si possono andare a toccare impostazioni di sistema ad un livello estremamente profondo, evitate le prove nubbe "perchè tanto è come DOS" e se lavorate su cose delicate utilizzate cd-live per i test. Eviterete sia danni sia eventuale sporcizia di sistema.
[ve l'ho detto, poi non venite a piangere...]

La possibilità di ottenere codice sorgente amplifica enormemente gli spazi di utilizzo di bash, che comunque resta l'anello di interazione tra sistema e utente qualunque linguaggio si utilizzi per creare programmi (bash richiama file in C, script in Perl ecc.).
Imparate a cercare e lavorare sui sorgenti facendo attenzione alle licenze (su cui ho già qualcosa in preparazione...)

Buon divertimento!
avatar
floatman

Messaggi : 844
Data d'iscrizione : 14.09.09

Visualizza il profilo http://myville.altervista.org

Torna in alto Andare in basso

Re: Guida al bash-scripting

Messaggio Da vikkio88 il Mer Set 16, 2009 1:51 pm

la mitica guida al bash scripting di floatman Very Happy...
avatar
vikkio88
Admin
Admin

Messaggi : 792
Data d'iscrizione : 14.09.09
Età : 29
Località : Palermo

Visualizza il profilo http://vikkio88.altervista.org

Torna in alto Andare in basso

Re: Guida al bash-scripting

Messaggio Da floatman il Mer Set 16, 2009 11:21 pm

Sì, qualcosina di carino lo voglio tenere Very Happy
Tra l'altro in questi giorni sto su Windows, quindi mi è difficile produrre qualcosa
avatar
floatman

Messaggi : 844
Data d'iscrizione : 14.09.09

Visualizza il profilo http://myville.altervista.org

Torna in alto Andare in basso

Re: Guida al bash-scripting

Messaggio Da Slack il Ven Set 18, 2009 3:15 pm

Ho appena finito di leggere la guida.
È fatta molto bene, complimenti, cerca di approfondire un po' di più se hai tempo.
avatar
Slack

Messaggi : 248
Data d'iscrizione : 14.09.09
Località : /home/slack

Visualizza il profilo http://linux.forumattivo.it

Torna in alto Andare in basso

Re: Guida al bash-scripting

Messaggio Da Archeor. il Gio Set 24, 2009 9:21 pm

Bella bella. Smile
avatar
Archeor.

Messaggi : 6
Data d'iscrizione : 23.09.09

Visualizza il profilo

Torna in alto Andare in basso

Re: Guida al bash-scripting

Messaggio Da floatman il Gio Set 24, 2009 10:43 pm

Grazie ^^
È deisamente elementare però credo di aver fatto un buon riassunto per chi vuole iniziare....sono stato un bel po' a faròa tra l'altro xD
avatar
floatman

Messaggi : 844
Data d'iscrizione : 14.09.09

Visualizza il profilo http://myville.altervista.org

Torna in alto Andare in basso

Re: Guida al bash-scripting

Messaggio Da Contenuto sponsorizzato


Contenuto sponsorizzato


Torna in alto Andare in basso

Torna in alto

- Argomenti simili

 
Permessi di questa sezione del forum:
Non puoi rispondere agli argomenti in questo forum