105.2 Lezione 1
Certificazione: |
LPIC-1 |
---|---|
Versione: |
5.0 |
Argomento: |
105 Shell e Script di Shell |
Obiettivo: |
105.2 Personalizzare o scrivere semplici script |
Lezione: |
1 di 2 |
Introduzione
L’ambiente shell di Linux consente l’uso di file — chiamati script — contenenti comandi disponibili nel sistema in combinazione con comandi incorporati nella shell per automatizzare le attività di un utente e/o di un sistema. In effetti, molte delle attività di manutenzione del sistema operativo vengono eseguite da script costituiti da sequenze di comandi, strutture decisionali e cicli condizionali. Sebbene gli script siano per la maggior parte del tempo destinati ad attività relative al sistema operativo stesso, sono utili anche per attività orientate all’utente, come la ridenominazione di massa dei file, la raccolta e l’analisi dei dati o qualsiasi attività da riga di comando altrimenti ripetitiva.
Gli script non sono altro che file di testo che si comportano come programmi. L'interprete, un programma vero e proprio, legge ed esegue le istruzioni elencate nel file di script. L’interprete può anche avviare una sessione interattiva in cui i comandi, inclusi gli script, vengono letti ed eseguiti non appena vengono immessi, come nel caso delle sessioni della shell di Linux. I file di script possono raggruppare tali istruzioni e comandi quando diventano troppo complessi per essere implementati come alias o come funzione di shell personalizzata. Inoltre, i file di script possono essere mantenuti come i programmi convenzionali e, essendo solo file di testo, possono essere creati e modificati con qualsiasi semplice editor di testo.
Struttura degli Script ed Esecuzione
Fondamentalmente, un file di script è una sequenza ordinata di comandi che deve essere eseguita da un interprete di comandi corrispondente. Il modo in cui un interprete legge un file di script varia e ci sono modi diversi per farlo in una sessione della shell Bash, ma l’interprete predefinito per un file di script sarà quello indicato nella prima riga dello script, subito dopo i caratteri #!
(noto come shebang). In uno script con le istruzioni per la shell Bash, la prima riga dovrebbe essere #!/bin/bash
. Indicando questa riga, l’interprete per tutte le istruzioni nel file sarà /bin/bash
. Ad eccezione della prima riga, tutte le altre righe che iniziano con il carattere cancelletto "#" verranno ignorate, quindi possono essere utilizzate per inserire promemoria e commenti. Anche le righe vuote vengono ignorate. Un file script di shell molto conciso può quindi essere scritto come segue:
#!/bin/bash # Uno script molto semplice echo "Cheers from the script file! Current time is: " date +%H:%M
Questo script ha solo due istruzioni per l’interprete /bin/bash
: il comando integrato echo
e il comando date
. Il modo più semplice per eseguire un file di script è eseguire l’interprete con il percorso dello script come argomento. Quindi, supponendo che l’esempio precedente sia stato salvato in un file di script chiamato script.sh
nella directory corrente, verrà letto e interpretato da Bash con il seguente comando:
$ bash script.sh Cheers from the script file! Current time is: 10:57
Il comando echo
aggiungerà automaticamente una nuova riga dopo aver visualizzato il contenuto, ma l’opzione -n
sopprimerà questo comportamento. Quindi, usando echo -n
nello script, l’output di entrambi i comandi apparirà nella stessa riga:
$ bash script.sh Cheers from the script file! Current time is: 10:57
Sebbene non sia richiesto, il suffisso .sh
aiuta a identificare gli script di shell quando si elencano e si cercano i file.
Tip
|
Bash chiamerà qualunque comando sia indicato dopo |
Se il file di script deve essere eseguito da altri utenti nel sistema, è importante verificare se sono impostate le autorizzazioni di lettura corrette. Il comando chmod o+r script.sh
darà il permesso di lettura a tutti gli utenti nel sistema, consentendo loro di eseguire script.sh
inserendo il percorso del file script come argomento del comando bash
. In alternativa, il file di script può avere il permesso di esecuzione impostato in modo che il file possa essere eseguito come un comando convenzionale. Il bit di esecuzione viene attivato sul file di script con il comando chmod
:
$ chmod +x script.sh
Con il bit di esecuzione abilitato, il file di script denominato script.sh
nella directory corrente può essere eseguito direttamente con il comando ./script.sh
. Gli script inseriti in una directory elencata nella variabile d’ambiente PATH
saranno accessibili anche senza il loro percorso completo.
Warning
|
Uno script che esegue azioni limitate può avere il suo permesso SUID attivato, quindi gli utenti ordinari possono anche eseguire lo script con i privilegi di root. In questo caso, è molto importante assicurarsi che nessun utente diverso da root abbia il permesso di scrivere nel file. In caso contrario, un utente normale potrebbe modificare il file per eseguire operazioni arbitrarie e potenzialmente dannose. |
Il posizionamento e il rientro dei comandi nei file di script non sono troppo rigidi. Ogni riga in uno script di shell verrà eseguita come un normale comando di shell, nella stessa sequenza in cui la riga appare nel file di script e le stesse regole che si applicano al prompt della shell si applicano anche a ciascuna riga di script individualmente. È possibile inserire due o più comandi nella stessa riga, separati da punto e virgola:
echo "Cheers from the script file! Current time is:" ; date +%H:%M
Sebbene questo formato possa essere a volte conveniente, il suo utilizzo è facoltativo, poiché i comandi sequenziali possono essere inseriti un comando per riga e verranno eseguiti proprio come se fossero separati da punto e virgola. In altre parole, il punto e virgola può essere sostituito da un nuovo carattere di riga nei file di script Bash.
Quando viene eseguito uno script, i comandi in esso contenuti non vengono eseguiti direttamente nella sessione corrente, ma vengono invece eseguiti da un nuovo processo Bash, chiamato sub-shell. Impedisce allo script di sovrascrivere le variabili di ambiente della sessione corrente e di lasciare le modifiche automatiche nella sessione corrente. Se l’obiettivo è eseguire il contenuto dello script nella sessione corrente della shell, allora dovrebbe essere eseguito con source script.sh
o . script.sh
(nota che c’è uno spazio tra il punto e il nome dello script).
Come accade con l’esecuzione di qualsiasi altro comando, il prompt della shell sarà nuovamente disponibile solo quando lo script avrà terminato la sua esecuzione e il suo codice di stato di uscita sarà disponibile nella variabile $?
. Per cambiare questo comportamento, in modo che anche la shell corrente termini quando lo script finisce, lo script — o qualsiasi altro comando — può essere preceduto dal comando exec
. Questo comando sostituirà anche il codice dello stato di uscita della sessione di shell corrente con il proprio.
Variabili
Le variabili negli script di shell si comportano allo stesso modo delle sessioni interattive, dato che l’interprete è lo stesso. Per esempio, il formato SOLUTION=42
(senza spazi attorno al segno di uguale) assegnerà il valore 42
alla variabile denominata SOLUTION
. Per convenzione, le lettere maiuscole vengono utilizzate per i nomi delle variabili, ma non è obbligatorio. Tuttavia, i nomi delle variabili non possono iniziare con caratteri non alfabetici.
Oltre alle normali variabili create dall’utente, gli script Bash hanno anche una serie di variabili speciali chiamate parametri o parameters. A differenza delle variabili ordinarie, i nomi dei parametri iniziano con un carattere non alfabetico che ne designa la funzione. Gli argomenti passati a uno script e altre informazioni utili sono memorizzati in parametri come $0
, $*
, $?
, ecc., Dove il carattere che segue il segno del dollaro indica le informazioni da recuperare:
$*
-
Tutti gli argomenti passati allo script.
$@
-
Tutti gli argomenti passati allo script. Se usato con virgolette doppie, come in
"$@"
, ogni argomento sarà racchiuso tra virgolette doppie. $#
-
Il numero degli argomenti.
$0
-
Il nome del file di script.
$!
-
PID dell’ultimo programma eseguito.
$$
-
PID della shell corrente.
$?
-
Codice numerico di stato di uscita dell’ultimo comando terminato. Per i processi standard POSIX, un valore numerico di
0
significa che l’ultimo comando è stato eseguito con successo, il che si applica anche agli script di shell.
Un parametro posizionale è un parametro indicato da una o più cifre, diverse dalla singola cifra 0
. Per esempio, la variabile $1
corrisponde al primo argomento fornito allo script (parametro posizionale uno), $2
corrisponde al secondo argomento e così via. Se la posizione di un parametro è maggiore di nove, deve essere referenziato con parentesi graffe, come in ${10}
, ${11}
, ecc.
Le variabili ordinarie, invece, hanno lo scopo di memorizzare valori inseriti manualmente o l’output generato da altri comandi. Il comando read
, per esempio, può essere utilizzato all’interno dello script per chiedere all’utente un input durante l’esecuzione dello script:
echo "Do you want to continue (y/n)?" read ANSWER
Il valore restituito verrà memorizzato nella variabile ANSWER
. Se il nome della variabile non viene fornito, per impostazione predefinita verrà utilizzato il nome della variabile REPLY
. È anche possibile utilizzare il comando read
per leggere più di una variabile contemporaneamente:
echo "Type your first name and last name:" read NAME SURNAME
In questo caso, ogni termine separato da spazi verrà assegnato rispettivamente alle variabili NAME
e SURNAME
. Se il numero di termini dati è maggiore del numero di variabili, i termini in eccesso verranno memorizzati nell’ultima variabile. Lo stesso read
può mostrare il messaggio all’utente con l’opzione -p
, rendendo ridondante il comando echo
in questo caso:
read -p "Type your first name and last name:" NAME SURNAME
Gli script che eseguono attività di sistema richiedono spesso informazioni fornite da altri programmi. La notazione backtick può essere utilizzata per memorizzare l’output di un comando in una variabile:
$ OS=`uname -o`
Nell’esempio, l’output del comando uname -o
sarà memorizzato nella variabile OS
. Un risultato identico verrà prodotto con $()
:
$ OS=$(uname -o)
La lunghezza di una variabile, ovvero la quantità di caratteri che contiene, viene restituita anteponendo un cancelletto #
prima del nome della variabile. Questa caratteristica, tuttavia, richiede l’uso della sintassi delle parentesi graffe per indicare la variabile:
$ OS=$(uname -o) $ echo $OS GNU/Linux $ echo ${#OS} 9
Bash dispone anche di variabili array unidimensionali, quindi un insieme di elementi correlati può essere memorizzato con un unico nome di variabile. Ogni elemento in un array ha un indice numerico, che deve essere utilizzato per scrivere e leggere i valori nell’elemento corrispondente. Diversamente dalle variabili ordinarie, gli array devono essere dichiarati con il comando integrato di Bash declare
. Per esempio, per dichiarare una variabile denominata SIZES
come un array:
$ declare -a SIZES
Gli array possono anche essere dichiarati implicitamente quando popolati da un elenco predefinito di elementi, utilizzando la notazione tra parentesi:
$ SIZES=( 1048576 1073741824 )
Nell’esempio, i due valori interi grandi sono stati memorizzati nell’array SIZES
. Gli elementi della matrice devono essere referenziati utilizzando parentesi graffe e parentesi quadre, altrimenti Bash non modificherà o visualizzerà l’elemento correttamente. Poiché gli indici dell’array iniziano da 0, il contenuto del primo elemento è in ${SIZES[0]}
, il secondo elemento è in ${SIZES[1]}
e così via:
$ echo ${SIZES[0]} 1048576 $ echo ${SIZES[1]} 1073741824
A differenza della lettura, la modifica del contenuto di un elemento dell’array viene eseguita senza le parentesi graffe (Per esempio, SIZES[0]=1048576
). Come con le variabili ordinarie, la lunghezza di un elemento in un array viene restituita con il carattere cancelletto (per esempio, ${#SIZES[0]}
per la lunghezza del primo elemento nell’array SIZES
). Il numero totale di elementi in un array viene restituito se @
o *
sono usati come indice:
$ echo ${#SIZES[@]} 2 $ echo ${#SIZES[*]} 2
Gli array possono anche essere dichiarati utilizzando l’output di un comando come elementi iniziali tramite la sostituzione del comando. Il seguente esempio mostra come creare un array Bash i cui elementi sono i filesystem supportati dal sistema corrente:
$ FS=( $(cut -f 2 < /proc/filesystems) )
Il comando cut -f 2 < /proc/filesystems
mostrerà tutti i filesystem attualmente supportati dal kernel in esecuzione (come elencato nella seconda colonna del file /proc/filesystems
), quindi l’array FS
ora contiene un elemento per ogni filesystem supportato. Qualsiasi contenuto di testo può essere utilizzato per inizializzare un array poiché, per impostazione predefinita, qualsiasi termine delimitato da caratteri space, tab o newline diventerà un elemento dell’array.
Tip
|
Bash tratta ogni carattere di |
Espressioni Aritmetiche
Bash fornisce un metodo pratico per eseguire operazioni aritmetiche su interi con il comando incorporato expr
. Due variabili numeriche, per esempio $VAL1
e $VAL2
, possono essere aggiunte insieme al seguente comando:
$ SUM=`expr $VAL1 + $VAL2`
Il valore risultante dell’esempio sarà disponibile nella variabile $SUM
. Il comando expr
può essere sostituito da $(())
, quindi l’esempio precedente può essere riscritto come SUM=$(($VAL1+$VAL2))
. Le espressioni di potenza sono consentite anche con l’operatore doppio asterisco, quindi la precedente dichiarazione di array SIZES=(1048576 1073741824)
potrebbe essere riscritta come SIZES=( $((1024**2)) $((1024**3)) )
.
La sostituzione dei comandi può essere utilizzata anche nelle espressioni aritmetiche. Per esempio, il file /proc/meminfo
contiene informazioni dettagliate sulla memoria di sistema, incluso il numero di byte liberi nella RAM:
$ FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` ))
L’esempio mostra come utilizzare il comando sed
per analizzare il contenuto di /proc/meminfo
all’interno dell’espressione aritmetica. La seconda riga del file /proc/meminfo
contiene la quantità di memoria libera in migliaia di byte, quindi l’espressione aritmetica la moltiplica per 1000 per ottenere il numero di byte liberi nella RAM.
Esecuzione Condizionale
Alcuni script di solito non hanno lo scopo di eseguire tutti i comandi nel file di script, ma solo quei comandi che corrispondono a criteri predefiniti. Per esempio, uno script di manutenzione può inviare un messaggio di avviso all’e-mail dell’amministratore solo se l’esecuzione di un comando non riesce. Bash fornisce metodi specifici per valutare il successo dell’esecuzione dei comandi e strutture condizionali generali, più simili a quelle che si trovano nei linguaggi di programmazione più diffusi.
Separando i comandi con &&
, il comando a destra verrà eseguito solo se il comando a sinistra non ha riscontrato un errore, cioè se il suo stato di uscita era uguale a 0
:
COMMAND A && COMMAND B && COMMAND C
Il comportamento opposto si verifica se i comandi sono separati da ||
. In questo caso, il comando seguente verrà eseguito solo se il comando precedente ha riscontrato un errore, cioè se il suo codice di stato restituito è diverso da 0.
Una delle caratteristiche più importanti di tutti i linguaggi di programmazione è la capacità di eseguire comandi in base a condizioni definite in precedenza. Il modo più semplice per eseguire comandi in modo condizionale è usare il comando incorporato di Bash if
, che esegue uno o più comandi solo se il comando dato come argomento restituisce un codice di stato 0 (successo). Un altro comando, test
, può essere usato per valutare molti differenti criteri speciali, quindi è usato principalmente insieme a if
. Nell’esempio seguente, il messaggio Confirmed: /bin/bash is executable.
verrà visualizzato solo se il file /bin/bash
esiste ed è eseguibile:
if test -x /bin/bash ; then echo "Confirmed: /bin/bash is executable." fi
L’opzione -x
fa sì che il comando test
restituisca un codice di stato 0 solo se il percorso specificato è un file eseguibile. L’esempio seguente mostra un altro modo per ottenere lo stesso identico risultato, poiché le parentesi quadre possono essere utilizzate in sostituzione di test
:
if [ -x /bin/bash ] ; then echo "Confirmed: /bin/bash is executable." fi
L’istruzione else
è opzionale alla struttura if
e può, se presente, definire un comando o una sequenza di comandi da eseguire se l’espressione condizionale non è vera:
if [ -x /bin/bash ] ; then echo "Confirmed: /bin/bash is executable." else echo "No, /bin/bash is not executable." fi
Le strutture if
devono sempre finire con fi
, quindi l’interprete di Bash sa dove terminano i comandi condizionali.
Output di uno Script
Anche quando lo scopo di uno script coinvolge solo operazioni orientate ai file, è importante visualizzare i messaggi relativi allo stato di avanzamento nell'output standard, in modo che l’utente sia informato di eventuali problemi e possa eventualmente utilizzare quei messaggi per generare i log delle operazioni.
Il comando integrato di Bash echo
è comunemente usato per visualizzare semplici stringhe di testo, ma fornisce anche alcune funzionalità estese. Con l’opzione -e
, il comando echo
è in grado di visualizzare caratteri speciali usando sequenze di escape (una sequenza di backslash che designa un carattere speciale). Per esempio:
#!/bin/bash # Get the operating system's generic name OS=$(uname -o) # Get the amount of free memory in bytes FREE=$(( 1000 * `sed -nre '2s/[^[:digit:]]//gp' < /proc/meminfo` )) echo -e "Operating system:\t$OS" echo -e "Unallocated RAM:\t$(( $FREE / 1024**2 )) MB"
Sebbene l’uso delle virgolette sia opzionale quando si usa echo
senza opzioni, è necessario aggiungerle quando si usa l’opzione -e
, altrimenti i caratteri speciali potrebbero non essere visualizzati correttamente. Nello script precedente, entrambi i comandi echo
usano il carattere di tabulazione \t
per allineare il testo, ottenendo il seguente output:
Operating system: GNU/Linux Unallocated RAM: 1491 MB
Il carattere di nuova riga \n
può essere utilizzato per separare le righe di output, quindi lo stesso identico output si ottiene combinando i due comandi echo
in uno solo:
echo -e "Operating system:\t$OS\nUnallocated RAM:\t$(( $FREE / 1024**2 )) MB"
Sebbene sia adatto per visualizzare la maggior parte dei messaggi di testo, il comando echo
potrebbe non essere adatto per visualizzare modelli di testo più specifici. Il comando integrato di Bash printf
dà più controllo su come visualizzare le variabili. Il comando printf
usa il primo argomento come formato dell’output, dove i segnaposto saranno sostituiti dai seguenti argomenti nell’ordine in cui appaiono nella riga di comando. Per esempio, il messaggio dell’esempio precedente potrebbe essere generato con il seguente comando printf
:
printf "Operating system:\t%s\nUnallocated RAM:\t%d MB\n" $OS $(( $FREE / 1024**2 ))
Il segnaposto %s
è destinato per il contenuto di testo (sarà rimpiazzato dalla variabile $OS
) e il segnaposto %d
è destinato a numeri interi (sarà rimpiazzato dal numero risultante di megabyte liberi nella RAM ). printf
non aggiunge un carattere di nuova riga alla fine del testo, quindi il carattere di nuova riga \n
dovrebbe essere posizionato alla fine del modello se necessario. L’intero modello deve essere interpretato come un singolo argomento, quindi deve essere racchiuso tra virgolette.
Tip
|
Il formato di sostituzione del segnaposto eseguita da |
Con printf
, le variabili sono poste al di fuori del pattern di testo, il che rende possibile memorizzarlo in una variabile separata:
MSG='Operating system:\t%s\nUnallocated RAM:\t%d MB\n' printf "$MSG" $OS $(( $FREE / 1024**2 ))
Questo metodo è particolarmente utile per visualizzare formati di output distinti, a seconda delle esigenze dell’utente. Rende più semplice, per esempio, scrivere uno script che utilizza un pattern di testo distinto se l’utente richiede un elenco CSV (Comma Separated Values) piuttosto che un messaggio di output predefinito.
Esercizi Guidati
-
L’opzione
-s
per il comandoread
è utile per inserire le password, poiché non mostrerà il contenuto digitato sullo schermo. Come potrebbe essere usato il comandoread
per memorizzare l’input dell’utente nella variabilePASSWORD
nascondendo il contenuto digitato? -
L’unico scopo del comando
whoami
è quello di visualizzare il nome utente dell’utente che lo ha eseguito, quindi viene utilizzato principalmente all’interno degli script per identificare l’utente che lo sta eseguendo. All’interno di uno script Bash, come potrebbe essere memorizzato l’output del comandowhoami
nella variabile chiamataWHO
? -
Quale operatore Bash dovrebbe esserci tra i comandi
apt-get dist-upgrade
esystemctl reboot
se l’utente root vuole eseguiresystemctl reboot
solo seapt-get dist-upgrade
è terminato con successo?
Esercizi Esplorativi
-
Dopo aver tentato di eseguire uno script Bash appena creato, un utente riceve il seguente messaggio di errore:
bash: ./script.sh: Permission denied
Considerando che il file
./script.sh
è stato creato dallo stesso utente, quale potrebbe essere la probabile causa di questo errore? -
Supponiamo che un file di script chiamato
do.sh
sia eseguibile e il collegamento simbolico chiamatoundo.sh
punti ad esso. Dall’interno dello script, come potresti identificare se il nome del file chiamante erado.sh
oundo.sh
? -
In un sistema con un servizio di posta elettronica configurato correttamente, il comando
mail -s "Maintenance Error" root <<<"Scheduled task error"
invia il messaggio di posta elettronica di avviso all’utente root. Tale comando potrebbe essere utilizzato in attività non assistite, come cronjob, per informare l’amministratore di sistema di un problema imprevisto. Scrivete un costrutto if che eseguirà il suddetto comandomail
se lo stato di uscita del comando precedente - -qualunque esso sia — non ha successo.
Sommario
Questa lezione copre i concetti base per la comprensione e la scrittura di script nella shell Bash. Gli script di shell sono una parte fondamentale di qualsiasi distribuzione Linux in quanto offrono un modo molto flessibile per automatizzare le attività dell’utente e del sistema. La lezione segue i seguenti passaggi:
-
Struttura dello script di shell e autorizzazioni corrette del file di script
-
Parametri di script
-
Utilizzo di variabili per leggere l’input dell’utente e memorizzare l’output dei comandi
-
Bash array
-
Test semplici ed esecuzione condizionale
-
Formattazione dell’output
I comandi e le procedure affrontati sono stati:
-
Notazione incorporata di Bash per la sostituzione dei comandi, espansione di array e espressioni aritmetiche
-
Esecuzione condizionale di comandi con gli operatori
||
e&&
-
echo
-
chmod
-
exec
-
read
-
declare
-
test
-
if
-
printf
Risposte agli Esercizi Guidati
-
L’opzione
-s
per il comandoread
è utile per inserire le password, poiché non mostrerà il contenuto digitato sullo schermo. Come potrebbe essere usato il comandoread
per memorizzare l’input dell’utente nella variabilePASSWORD
nascondendo il contenuto digitato?read -s PASSWORD
-
L’unico scopo del comando
whoami
è quello di visualizzare il nome utente dell’utente che lo ha eseguito, quindi viene utilizzato principalmente all’interno degli script per identificare l’utente che lo sta eseguendo. All’interno di uno script Bash, come potrebbe essere memorizzato l’output del comandowhoami
nella variabile chiamataWHO
?WHO=`whoami`
oWHO=$(whoami)
-
Quale operatore Bash dovrebbe esserci tra i comandi
apt-get dist-upgrade
esystemctl reboot
se l’utente root vuole eseguiresystemctl reboot
solo seapt-get dist-upgrade
è terminato con successo?L’operatore
&&
, come inapt-get dist-upgrade && systemctl reboot
.
Risposte agli Esercizi Esplorativi
-
Dopo aver tentato di eseguire uno script Bash appena creato, un utente riceve il seguente messaggio di errore:
bash: ./script.sh: Permission denied
Considerando che il file
./script.sh
è stato creato dallo stesso utente, quale potrebbe essere la probabile causa di questo errore?Il file
./script.sh
non possiede il permesso di esecuzione attivo. -
Supponiamo che un file di script chiamato
do.sh
sia eseguibile e il collegamento simbolico chiamatoundo.sh
punti ad esso. Dall’interno dello script, come potresti identificare se il nome del file chiamante erado.sh
oundo.sh
?La variabile speciale
$0
contiene il nome del file utilizzato per richiamare lo script. -
In un sistema con un servizio di posta elettronica configurato correttamente, il comando
mail -s "Maintenance Error" root <<<"Scheduled task error"
invia il messaggio di posta elettronica di avviso all’utente root. Tale comando potrebbe essere utilizzato in attività non assistite, come cronjob, per informare l’amministratore di sistema di un problema imprevisto. Scrivete un costrutto if che eseguirà il suddetto comandomail
se lo stato di uscita del comando precedente - -qualunque esso sia — non ha successo.if [ "$?" -ne 0 ]; then mail -s "Maintenance Error" root <<<"Scheduled task error"; fi