"Se ci sono due o più modi di fare una cosa,
e uno di questi modi può condurre a una catastrofe,
allora qualcuno la farà in quel modo.

(Edward Murphy)

Linux Shell script per principianti - Come iniziare a programmare con la shell

Dom, 01/04/2012 - 00:00 -- arturu
Linux shell

Recentemente mi è capitato di dover spiegare ad un amico come funziono gli script shell linux. Uno script shell è utile quando bisogna fare delle operazioni ripetitive, pianificare delle operazioni da eseguire periodicamente, estendere le funzionalità di linux, ecc. Cercherò in questo articolo di spiegare un po' le basi della "programmazione" con la shell linux. Non è niente di trascendentale o tremendamente complicato, basta solo un po' di ragionamento e pazienza

Le basi

Come prima cosa bisogna creare un nuovo file "scrip_esempio.sh" e dare ad esso i permessi di esecuzione (tramite interfaccia o con "chmod +x"), l'estensione ".sh" non è strettamente necessaria ma ci permette di riconoscere fin da subito i file che contengono script. La prima istruzione che andremo ad inserire dentro il notro file stabilisce quale shell dobbiamo eseguire, ed è proprio questa istruzione che fa "vivere" il nostro script shell,

#!/bin/bash

questo concetto per i neofiti sembra abbastanza assurdo, ma volendo semplificare le cose, basta pensare che la prima istruzione serve ad indicare il "programma" che interpreterà il nostro script. In molti script si può trovare anche la seguente dichiarazione: "#!/bin/sh"; la shell "sh", senza scatenare guerre di religione, è ormai obsoleta, meglio usare "bash" che ha un set di istruzioni molto più ampio rispetto alla precedente, quindi, se in caso uno script non esegue alcune operazioni o non riconosce alcuni comandi controllate con quale "programma" state eseguendo lo script.

Come concetto di base uno script shell permette di eseguire una sequenza di "comandi", programmi o altri script, organizzati in strutture usate nella programmazione. Quindi una conoscenza base dei comandi è necessaria, comunque in questa guida spiegherò passo passo quello che andremo a fare, invece, per chi vuole approfondire può consultare i seguenti link:

Tutti i commenti sono preceduti dal simbolo cancelletto

#tutta la linea è commentata
cd cartella #tutto dopo il cancelletto è un commento

Vediamo ora un un semplice esempio

#!/bin/bash
#mi sposto nella cartella documenti, il simbolo "~" indica l'home directory dell'utente che esegue lo script
cd ~/Documenti

#mostro il contenuto con informazioni sui permessi, la data e il peso
ls -lah

Variabili, cicli, loop, ecc.

Oltre che lanciare i comandi singoli possiamo eseguire dei loop, cicli e condizioni, una rapida carrellata:

#inizializza una variabile
variabile="variabile"
#stampa una variabile
echo $variabile
#salva output di un programma in una variabile
variabile=$(ls /home/Utente/Scrivania)
#preleva input utente e lo salva in una variabile e poi lo stampa
echo -e "Inserisci una parola: \c"

read  parola
echo $parola
#preleva input utente e lo salva in una lista (array) e poi stampa gli elementi
echo -e "Inserisci delle parole:"

#l'opzione -a inserisce l'input in array
read -a parola

#stampo in modo grossolano, si dovrebbe fare un ciclo for ma è un esempio
echo "hai inserito: ${parola[0]}, ${parola[1]}, ${parola[2]}"
#un ciclo if elseif ed else
if [ $variabile = $condizione ];
  then
    echo "condizione true"

elif [ $variabile = $condizione ];
  then
    echo "condizione true di elseif"
else
    echo "condizione else"
fi
#un ciclo while
while [ $loop == "true" ]
do
   echo "questo è un loop"
done
#Dichiaro una lista (array) di 4 elementi, NB nota le virgolette
lista=( Debian Redhat 'Ubuntu 12.04' 'Linux 3.2' )

# recupero il numero degli elementi
n_elementi=${#lista[@]}

# for loop
for (( i=0;i<$n_elementi;$i++))
do
    echo "$lista[$i]"
done

Esempi utili

Lavorano con gli script spesso abbiamo bisogno di leggere, spostare, cercare e convertire file ecc. Ecco un po' di esempi veloci

Operazioni sui file

#!/bin/bash
file="prova.txt"

if [ -e $file ]; #per l'opzione -e consulta la legenda
  then
    echo "File esiste"
  else
    echo "File non esiste"
fi

# -b filename 	Block special file
# -c filename 	Special character file
# -d directoryname 	Check for directory existence
# -e filename 	Check for file existence
# -f filename 	Check for regular file existence not a directory
# -G filename 	Check if file exists and is owned by effective group ID.
# -g filename 	true if file exists and is set-group-id.
# -k filename 	Sticky bit
# -L filename 	Symbolic link
# -O filename 	True if file exists and is owned by the effective user id.
# -r filename 	Check if file is a readable
# -S filename 	Check if file is socket
# -s filename 	Check if file is nonzero size
# -u filename 	Check if file set-ser-id bit is set
# -w filename 	Check if file is writable
# -x filename 	Check if file is executable

Un loop utile

#!/bin/bash

# cicla tutti i file in una cartella
for f in $( ls ~/Scrivania );
do
    echo $f
done

Uno script che rinomina file (sintassi avanzata)

#!/bin/bash
# Questo script cerca nei nomi dei file e sostiuisce tutti gli spazi con "_"
# la cartella da cercare,
cartella="~/musica"

# Il simbolo pipe "|" redirige lo STDOUT (standard output) nello STDIN (standard input) di un altro
# il programma "find" serve per cercare "-type f" cerca solo file
find $cartella -type f | while read file; do

# [:space:] è usato nelle espressioni regolari per indicare lo spazio
  if [[ "$file" = *[[:space:]]* ]];
    then
      # in un unico colpo rinomina il file usando "echo $file | tr ' ' '_'" come seconda opzione di "mv"
      # praticamente stampa il nome del file modificato tramite "tr" è un programma che manipola stringhe
      mv "$file" `echo $file | tr ' ' '_'`
  fi;
# fine del loop
done

Uno script di Conversione da .doc(x) a .pdf dato un elenco di ricerca

In un file .csv abbiamo su ogni riga il nome di un file .doc da convertire in .pdf

#!/bin/bash
#cartella di ricerca
ricerca="~/documenti"

#il file con l'elenco
file_ricerca="~/elenco.csv"

#una cartella per salvare i file salvati
cartella_salvataggio="~/salvataggio_pdf"

#controllo se la cartella di salvataggio esiste altrimenti ne creo una
if [ -d $cartella_salvataggio ]; then
	echo "La cartella esiste"
else
	mkdir cartella_salvataggio
fi
#mi sposto nella cartella di ricerca, altrimenti find cerca a partire dalla posizione dove si trova lo script
cd ricerca

#ogni ciclo inserisce una linea del file di ricerca nella variabile "linea", vedi chiusura per la lettura
while read linea;
do
  #"find"il comando che cerca, -iname è l'opzione che non distingue tra maiscole e minuscole
  # la forma "variabile=$(comando)" permette di salvare l'out di un comando in una variabile
  file_doc=$(find -iname $linea -type f)

  #controllo se la ricerca è vuota altrimenti converto, nel ciclo if metto la variabile tra "", se ci stanno spazi nel nome può generare errori strani
  if [ "$file_doc" == ""]; then
     echo "Il file $linea non si trova in questa cartella"
  else
    # copio il file trovato nella cartella di salvataggio
    cp "$file_doc" "$cartella_salvataggio/$file_doc" 

    # il programma unoconv di solito non è installato con la suite openoffice, in caso bisogna installarlo
    # su i sistemi Debian "sudo apt-get install unoconv" per installarlo
    unoconv -f pdf "$cartella_salvataggio/$file_doc"

    #cancello il file doc originale nella cartella di salvataggio che ormai non serve più
    rm "$cartella_salvataggio/$file_doc"
  fi
# faccio leggere il file di ricerca a while
done < $file_ricerca

#mi sposto ad un livello superiore della cartella di salvataggio
cd $cartella_salvataggio
cd ..

#comprimo tutto in un archivio
tar -zcvf file_convertiti.tar.gz $cartella_salvataggio

Conclusioni

Per chi è proprio all'assciutto, per eseguire uno script basta spostarsi nella cartella dove si trova con "cd", e successivamente scrivendo il nome dello script preceduto da "./" (significa che si intende far partire un programma dalla posizione corrente):

cd ~/cartella_dove/si_trova/lo_script
./nome_script.sh

Di sicuro questa non è una guida esaustiva sul grande universo dello scripting shell, spero che sia almeno utile per superare il primo scoglio: riuscire a capire come iniziare.
Per il resto c'è google, sa tutto... :)
Ciao