Bash scripting: differenze tra le versioni

Vai alla navigazione Vai alla ricerca
(introduzione più estesa con esempi, prima riga e sintassi base dei comandi più importanti)
(aggiunte istruzioni composte e di gestione di file descriptor e segnali; ampliate le altre sezioni)
Riga 1: Riga 1:
{{Versioni_compatibili}}
{{Versioni_compatibili}}
=Introduzione=
=Introduzione=
Questa non è una guida completa, per la vastità dell'argomento trattato. In particolare non sono trattati alcuni comandi importanti:
Questa non è una guida completa, per la vastità dell'argomento trattato. In particolare non sono trattati alcuni comandi importanti introdotti proprio da questa shell:
* di condizione ed esecuzione condizionata avanzata (<code>[[</code>, <code>((</code>, <code>case</code>);
* di condizione ed esecuzione condizionata avanzata (<code>[[</code>, <code>((</code>, <code>case</code>);
* per definire funzioni (<code>... ()</code> / <code>function ...</code>);
* per effettuare il parsing degli argomenti (<code>getopts</code>);
* per effettuare il parsing degli argomenti (<code>getopts</code>);
* di gestione e invio di segnali (<code>trap</code>, <code>kill</code>);
* di redirezione here-document e di processo;
* di gestione dei file descriptor;
* modificatore declare (per interi, array e array associativi);
* modificatori di variabili (<code>readonly</code>, <code>local</code>, <code>declare</code>);
* ecc...
* ecc...


Lo scopo della guida è invece, partendo dai concetti più basilari, di evidenziare i comportamenti più distintivi e facili da sbagliare di Bash, con enfasi particolare sulle espansioni di stringhe, estremamente diverse da altri linguaggi di programmazione. Così da poter passare poi a guide più avanzate per completare l'apprendimento dello scripting in Bash.
Lo scopo della guida è invece, partendo dai concetti più basilari, di evidenziare i comportamenti più distintivi e facili da sbagliare di '''bash''', con enfasi particolare sulle espansioni di stringhe, estremamente diverse da altri linguaggi di programmazione. Così da passare poi a guide più avanzate. Si cercherà anche di presentare le istruzioni in una sintassi il più possibile compatibile con la shell '''sh'''.


Le spiegazioni fornite in questa sezione introduttiva su alcuni comandi fondamentali si limitano alla loro sintassi base, così che il loro impiego nelle sezioni successive possa essere facilmente compreso.
Quando si fa riferimento a comandi esterni, per conoscere la loro sintassi si può consultare il manuale (<code>man nome-comando-esterno</code>). La principale utilità dello scripting con la shell è proprio la semplicità di ampliarne le funzionalità richiamando altri eseguibili, quindi è consigliata anche la conoscenza dei principali (in particolare sulla [Guida_ai_comandi_da_terminale_-_Gestione_di_file_e_directory gestione di file e directory]).


Per l'uso interattivo della shell si rimanda a [[Bash tips]]. Si noti che l'espansione della history, che qui non è trattata, è attiva soltanto in modalità interattiva.
Per l'uso interattivo della shell si rimanda invece a [[Bash tips]]. Si noti che l'espansione della history, che qui non è trattata, è attiva soltanto in modalità interattiva mediante i caratteri speciali <code>!</code> e <code>^</code>, che di default non hanno invece nessun significato particolare all'interno di uno script.
 
==Breve storia della shell==
GNU '''bash''' (''Bourne-Again SHell'') è una delle shell derivate da '''sh''' (la ''Bourne shell'', progettata da Stephen Bourne), la prima progettata per lo scripting. Bash è la scelta di default per l'uso interattivo su Debian e molte distribuzioni GNU/Linux, il che la rende una delle più diffuse, e incorpora molte nuove funzionalità rispetto a '''sh''', alcune derivanti da altre shell ('''csh''' e '''ksh''') e altre presenti soltanto in '''bash'''.
 
Quando si scrivono script con questa shell bisogna sapere che le nuove funzionalità comportano un costo, in termini di portabilità, rispetto agli script che si limitano alla sola sintassi prevista da ''POSIX'', lo standard che specifica come scrivere applicazioni funzionanti su tutti i sistemi UNIX e che per la shell prescrive le sole istruzioni supportate da '''sh'''. Infatti uno script scritto per '''sh''' secondo lo standard ''POSIX'' sarà supportato anche da altri sistemi operativi della famiglia UNIX e Unix-like che non hanno '''bash''' installato di default, come per esempio quelli della famiglia *BSD, a patto che anche i comandi esterni e le loro opzioni siano scelte secondo lo standard.
 
Inizialmente '''bash''' era usata anche per gli script di sistema (con <code>/bin/sh</code> che puntava a <code>/bin/bash</code>), ma per questa funzione è stata rimpiazzata da '''dash''' (''Debian Almquist Shell''): più veloce, con meno dipendenze di librerie e aderente molto più strettamente a ''POSIX''.
 
Per quanto possibile in questa guida, in presenza di sintassi alternative, si tenderà a prediligere quella presente in ''POSIX'' e si segnalerà ogni volta che un'istruzione è presente unicamente in '''bash'''.


==Come creare uno script==
==Come creare uno script==
Riga 30: Riga 37:


Inoltre è bene sapere che l'esecuzione di uno script in '''bash''', così come uno in '''sh''' (''POSIX''), di default non è interrotta in presenza di errori. È importante quindi controllare l'exit status dei comandi eseguiti, se possono fallire, con <code>if</code> oppure con gli operatori logici di concantenazione <code>&&</code> e <code>||</code>, che saranno introdotti.
Inoltre è bene sapere che l'esecuzione di uno script in '''bash''', così come uno in '''sh''' (''POSIX''), di default non è interrotta in presenza di errori. È importante quindi controllare l'exit status dei comandi eseguiti, se possono fallire, con <code>if</code> oppure con gli operatori logici di concantenazione <code>&&</code> e <code>||</code>, che saranno introdotti.
Fa eccezione l'istruzione <code>exec</code> che, se seguita da un eseguibile esterno (anche un altro script), fa eseguire al processo corrente il nuovo eseguibile. Le istruzioni successive nello script non verranno eseguite, e il valore di uscita dello script è determinato da quello del nuovo eseguibile.


===Commenti===
===Commenti===
Riga 35: Riga 44:


I commenti sono ovviamente ignorati dall'interprete, ma rendono più leggibile il codice.
I commenti sono ovviamente ignorati dall'interprete, ma rendono più leggibile il codice.
Si noti che lo stessa ''shebang'' introduce una riga di commento, così da poter essere riconosciuta dal sistema grazie al sistema dei ''magic pattern'' e allo stesso tempo essere ignorata dall'interprete.


===Il primo script===
===Il primo script===
Riga 73: Riga 84:
#! /bin/bash -e
#! /bin/bash -e
</pre>
</pre>
=Comandi essenziali=
I comandi introdotti in questa sezione sono descritti solo limitatamente alla loro sintassi base, così che il loro impiego nelle sezioni successive possa essere facilmente compreso.
La lettura della sezione può essere tralasciata, se si hanno già nozioni basilari di '''bash''', ma la parte sui comandi di output serve anche a giustificare la scelta di <code>printf</code> in luogo del più noto <code>echo</code> come unico comando di output e a spiegarne brevemente la sintassi, almeno per le invocazioni più comuni.


==Comandi di output: echo e printf==
==Comandi di output: echo e printf==
Riga 229: Riga 245:
fi
fi
</pre>
</pre>
===Ciclo condizionato===
Per eseguire un blocco di comandi se un comando ha successo e ripeterlo finché il comando continua ad avere successo, si può usare l'istruzione <code>while</code>. Proprio come <code>if</code> è spesso usata in congiunzione con <code>[...]</code> per ripetere un blocco di codice tutte le volte che la condizione espressa è vera.
Sintassi base (in congiunzione con <code>[...]</code>):
<pre>
while [ espressione-booleana ]
do
  ...
done
</pre>
Per esempio:
<pre>
num=
# ripeti finché non è soddisfatta la condizione (si noti il ! davanti a [...])
while ! [ "$num" -gt 0 ] 2> /dev/null
do
  printf %s "Inserisci intero > 0 e premi INVIO: "
  # assegna l'input dell'utente a $num
  read num
done
printf %s\\n "Hai inserito: ${num}"
</pre>
Oppure si può utilizzare un comando qualsiasi, per esempio per eseguire un ciclo infinito:
<pre>
while :
do
  ...
done
</pre>
Un ciclo può essere sempre:
* interrotto da <code>break</code>;
* ripreso dalla prima istruzione del blocco, dopo aver rivalutato l'exit status del comando dopo <code>while</code>, con <code>continue</code>.


=Variabili (stringhe)=
=Variabili (stringhe)=
In Bash ogni variabile di default è trattata come una stringa e, benché Bash supporti anche interi e array (indicizzati o associativi), questa sezione si limita al solo tipo base.
In '''bash''' ogni variabile di default è trattata come una stringa e, benché siano supportati anche interi e array (indicizzati o associativi), questa guida si limita al solo tipo base.


==Nomi di variabili==
==Nomi di variabili==
Riga 294: Riga 274:
var=~utente              # assegna la home di utente
var=~utente              # assegna la home di utente
</pre>
</pre>
===Modificatori===
Sono comandi interni che possono essere applicati soltanto a un nome di variabile (senza '''$''') o a un'assegnazione, e in quest'ultimo caso hanno effetto sulla variabile dopo l'avvenuta assegnazione.
; export : specifica che la variabile farà parte delle variabili d'ambiente (''environment'') dei comandi esterni eseguiti dallo script:
<pre>
var="stringa"
export var
# equivalente a:
export var="stringa"
# comando esterno
comando              # può accedere al contenuto di var
</pre>
È possibile definire una o più variabili nell'ambiente di un comando, senza farle ereditare a quelli successivi, semplicemente scrivendo le assegnazioni prima del nome del comando. Per esempio:
<pre>
var="stringa" comando # può accedere al contenuto di var
printf %s\\n "$var"  # $var è vuota!
</pre>
; readonly : specifica che la variabile (per convenzione scritta con caratteri maiuscoli) è di sola lettura e dev'essere trattata da quel punto in poi come una costante:
<pre>
VAR="valore"
readonly VAR
# equivalente a:
readonly VAR="valore"
VAR="altro valore"  # ERRORE! var ora è una costante
</pre>
Per convenzione le costanti sono poste tutte all'inizio dello script, prima anche di eventuali definizioni delle funzioni.


===Assegnazione dallo standard input===
===Assegnazione dallo standard input===
Con l'istruzione <code>read</code> è possibile assegnare a una o più variabili il contenuto di una riga dello standard input, che senza ridirezioni e pipe corrisponde a ciò che viene scritto da tastiera prima di un invio.
Con l'istruzione <code>read</code> è possibile assegnare a una o più variabili il contenuto di una riga dello standard input, che senza redirezioni e pipe corrisponde a ciò che viene scritto da tastiera prima di un invio.


Sintassi (base): <code>read nomevariabile [ ... ]</code>
Sintassi (base): <code>read nomevariabile [ ... ]</code>
Riga 324: Riga 331:
Due modi tipici per generare una lista di stringhe sono:
Due modi tipici per generare una lista di stringhe sono:
* con la variabile speciale <code>"$@"</code>, l'unica che se quotata si espande a una lista di stringhe;
* con la variabile speciale <code>"$@"</code>, l'unica che se quotata si espande a una lista di stringhe;
* con l'espansione di percorso tramite <code>*</code>, che sarà trattata in seguito.
* con l'espansione di percorso, che sarà trattata in seguito;


Per esempio:
Per esempio:
Riga 337: Riga 344:
</pre>
</pre>


Il ciclo <code>for</code>, proprio come <code>while</code>, può essere:
{{Warningbox | Se si utilizza l'espansione di comando per generare una lista di stringhe, bisogna assicurarsi che ogni stringa non contenga spazi oppure caratteri speciali che potrebbero essere espansi nuovamente (<code>* ? [ ]</code>). L'uso combinato con <code>find</code> in particolare è sconsigliato e quasi sempre sbagliato, a meno che non si sappia a priori che ogni file presente nel percorso scelto soddisfa tali condizioni.}}
 
Il ciclo <code>for</code> può essere:
* interrotto dall'istruzione <code>break</code>;
* interrotto dall'istruzione <code>break</code>;
* continuato saltando la corrente iterazione del ciclo con <code>continue</code>, che passa al prossimo elemento della lista, se presente, altrimenti esce.
* continuato saltando la corrente iterazione del ciclo con <code>continue</code>, che passa al prossimo elemento della lista, se presente, altrimenti esce.
Riga 356: Riga 365:
Ciò è ancora più importante quando si passa la variabile a un comando che agisce su un file indicato dalla variabile, il cui contenuto in presenza di spazi (comuni per i nomi di file degli utenti) potrebbe venir trattato come una lista di file.
Ciò è ancora più importante quando si passa la variabile a un comando che agisce su un file indicato dalla variabile, il cui contenuto in presenza di spazi (comuni per i nomi di file degli utenti) potrebbe venir trattato come una lista di file.


Esempio di codice che crea un backup di un file indicato da una variabile:
Esempio di codice che crea un backup di un file indicato da una variabile (tramite il comando esterno <code>cp</code>):
<pre>
<pre>
cp -- "$file" "${file}.bak"
cp -- "$file" "${file}.bak"
Riga 497: Riga 506:
Modificatori:
Modificatori:
* <code>${#var}</code> ritorna il numero di caratteri della stringa contenuta in $var. Espande sempre a una singola stringa;
* <code>${#var}</code> ritorna il numero di caratteri della stringa contenuta in $var. Espande sempre a una singola stringa;
* <code>${!var}</code> ritorna il contenuto della variabile, il cui nome è contenuto in $var (accesso indiretto);
* <code>${!var}</code> (non ''POSIX'') ritorna il contenuto della variabile, il cui nome è contenuto in $var (accesso indiretto);
* espande o assegna valori di default/alternativi;
* espande o assegna valori di default/alternativi;
* manipolatori di stringa (rimozione e sostituzione);
* manipolatori di stringa (rimozione, sostituzione, trasformazione in uppercase/lowercase).
* trasformazione in uppercase / lowercase.


Esempio:
Esempio:
Riga 512: Riga 520:


===Manipolazione delle stringhe===
===Manipolazione delle stringhe===
Nelle shell *nix, storicamente, la manipolazione delle stringhe viene fatto attraverso programmi esterni alla shell come sed, awk e perl. Questi programmi vengono ancora usati quando si vuole mantenere la compatibilità con la shell <code>'''sh'''</code> (''POSIX'') o per manipolazioni molto complesse.
Per manipolare una stringa, è possibile assegnarla a una variabile per poi effettuarne un'espansione di parametro che manipola la stringa, senza modificare il contenuto della variabile. Per esempio:
 
Se si usa Bash non è necessario usare nessun programma esterno, ma basta imparare i tre operatori fondamentali ed alcuni concetti di base, per poter fare tutte le manipolazioni più comuni direttamente sulle variabili.
 
Si assegna una stringa a una variabile e accedendola tramite la forma con le graffe, si può ricorrere a un modificatore che manipola la stringa (senza modificare il contenuto della variabile), ad esempio:
<pre>
<pre>
VAR="stringa-di-esempio"
VAR="stringa-di-esempio"
Riga 527: Riga 531:
ora il prefisso "stringa-" è stato eliminato anche dalla variabile VAR.
ora il prefisso "stringa-" è stato eliminato anche dalla variabile VAR.


I modificatori sono molti, ma possono essere facilmente ricordati se si imparano i tre fondamentali:
I modificatori sono molti, ma possono essere facilmente ricordati se si imparano i fondamentali:
* <code>#</code> sottrae dall'inizio della stringa ''(minimale)'';
* <code>#</code> sottrae dall'inizio della stringa ''(minimale)'';
* <code>%</code> sottrae dalla fine della stringa ''(minimale)'';
* <code>%</code> sottrae dalla fine della stringa ''(minimale)'';
* <code>/</code> sostituisce una sottostringa con un'altra ''(solo la prima volta che viene incontrata)''.
* <code>/</code> (non ''POSIX'') sostituisce una sottostringa con un'altra ''(solo la prima volta che viene incontrata)'';
* <code>^</code> (non ''POSIX'') trasforma in maiuscola la prima lettera della stringa ''(solo il primo carattere)'';
* <code>,</code> (non ''POSIX'') trasforma in minuscola la prima lettera della stringa ''(solo il primo carattere)''.


Questi operatori sono minimali, questo vuol dire che se si usano le espressioni regolari per indicare la sottostringa (da eliminare o sostituire) verrà individuata in caso di ambiguità la sottostringa più piccola (o solo la prima nel caso della sostituzione).  
Questi operatori sono minimali, questo vuol dire che se si usano le espressioni regolari per indicare la sottostringa (da eliminare, sostituire o trasformare) verrà individuata in caso di ambiguità la sottostringa più piccola (o solo la prima nel caso di sostituzione o trasformazione).  


Per ottenere gli operatori massimali basta raddoppiare il simbolo:
Per ottenere gli operatori massimali basta raddoppiare il simbolo:
* <code>##</code> sottrae dall'inizio della stringa ''(massimale)'';
* <code>##</code> sottrae dall'inizio della stringa ''(massimale)'';
* <code>%%</code> sottrae dalla fine della stringa ''(massimale)'';
* <code>%%</code> sottrae dalla fine della stringa ''(massimale)'';
* <code>//</code> sostituisce una sottostringa con un'altra ''(tutte le volte che viene incontrata)''.
* <code>//</code> (non ''POSIX'') sostituisce una sottostringa con un'altra ''(tutte le volte che viene incontrata)'';
* <code>^^</code> (non ''POSIX'') trasforma in maiuscola la stringa ''(tutta)'';
* <code>,,</code> (non ''POSIX'') trasforma in minuscola la stringa ''(tutta)''.


Gli operatori massimali cercano di individuare la sottostringa più grande che corrisponde all'espressione regolare (nel caso del modificatore '''//''' tutte le sottostringhe vengono sostituite).
Gli operatori massimali cercano di individuare la sottostringa più grande che corrisponde all'espressione regolare, mentre nel caso del modificatore '''//''' tutte le sottostringhe vengono sostituite, e nel caso della trasformazione tutta la stringa e non solo il primo carattere.


Si noti che le stringhe interne a un'espansione di parametro possono essere delle variabili, ma non altre espansioni di parametro:
Si noti che le stringhe interne a un'espansione di parametro possono essere delle variabili, ma non altre espansioni di parametro:
Riga 549: Riga 557:
</pre>
</pre>


Per una spiegazione dettagliata di tutti i modificatori e anche di altri modi di manipolare le stringhe in Bash (ad esempio <code>expr</code>) vedere:
Le espressioni regolari supportate sono le stesse permesse nelle espansioni di parametro e con lo stesso significato (ossia: <code>? * [ ]</code>), e con gli operatori di sottrazione c'è differenza tra quelli minimali e massimali soltanto se il pattern contiene uno o più <code>*</code>, che è l'unico carattere speciale che può sostituire un numero qualsiasi (zero o più) di caratteri.
* [http://www.tldp.org/LDP/abs/html/string-manipulation.html Advanced Bash-Scripting Guide: Manipulating Strings]
 
====Esempio: alternativa a basename====
Quando in uno script ci si deve riferire al nome dello script stesso, è usuale utilizzare il comando esterno <code>'''basename'''</code>. Una possibile alternativa:
<pre>
usage () {
  printf %s\\n "Usage: ${0##*/}"
}
</pre>


====Esempi: manipolazione delle stringhe====
====Esempi: manipolazione delle stringhe====
<pre>
<pre>
VAR="questa.sarebbe.una.stringa.di.esempio"
VAR="questa.sarebbe.UNA.stringa.di.esempio"
    
    
                             # Risultato:
                             # Risultato:
    
    
printf %s\\n "${VAR#*.}"    # --> sarebbe.una.stringa.di.esempio
printf %s\\n "${VAR#*.}"    # sarebbe.UNA.stringa.di.esempio
printf %s\\n "${VAR##*.}"    # --> esempio
printf %s\\n "${VAR##*.}"    # esempio
    
    
printf %s\\n "${VAR%.*}"    # --> questa.sarebbe.una.stringa.di
printf %s\\n "${VAR%.*}"    # questa.sarebbe.UNA.stringa.di
printf %s\\n "${VAR%%.*}"    # --> questa
printf %s\\n "${VAR%%.*}"    # questa
    
    
printf %s\\n "${VAR/st/ST}"  # --> queSTa.sarebbe.una.stringa.di.esempio
printf %s\\n "${VAR/st/LL}"  # queLLa.sarebbe.UNA.stringa.di.esempio
printf %s\\n "${VAR//st/ST}" # --> queSTa.sarebbe.una.STringa.di.esempio
printf %s\\n "${VAR//st/LL}" # queLLa.sarebbe.UNA.LLringa.di.esempio
 
printf %s\\n "${VAR^*}"     # Questa.sarebbe.UNA.stringa.di.esempio
printf %s\\n "${VAR^^*}"    # QUESTA.SAREBBE.UNA.STRINGA.DI.ESEMPIO
 
printf %s\\n "${VAR,*}"      # questa.sarebbe.UNA.stringa.di.esempio (invariata)
printf %s\\n "${VAR,,*}"    # questa.sarebbe.una.stringa.di.esempio
</pre>
</pre>


====Esempio: alternativa a basename====
Per una spiegazione dettagliata di tutti i modificatori e anche di altri modi di manipolare le stringhe in Bash (ad esempio <code>expr</code>) vedere:
Quando in uno script ci si deve riferire al nome dello script stesso, è usuale utilizzare il comando esterno <code>'''basename'''</code>. Una possibile alternativa:
* [http://www.tldp.org/LDP/abs/html/string-manipulation.html Advanced Bash-Scripting Guide: Manipulating Strings]
<pre>
usage () {
  printf %s\\n "Usage: ${0##*/}"
}
</pre>


==Espansione di comando==
==Espansione di comando==
Riga 627: Riga 643:
nr="
nr="
"                    # funziona, ma occupa più righe e rompe l'indentazione
"                    # funziona, ma occupa più righe e rompe l'indentazione
nr=$'\n'            # funziona (con Bash) ed è il modo consigliato
nr=$'\n'            # funziona (non POSIX) ed è il modo consigliato in bash


# a titolo esemplicativo per l'espansione di comando
# a titolo esemplicativo per l'espansione di comando
Riga 633: Riga 649:
nr="$(printf \\n)"  # SBAGLIATO, $nr è sempre vuota
nr="$(printf \\n)"  # SBAGLIATO, $nr è sempre vuota


# una possibile soluzione...
# una possibile soluzione (POSIX)
nr=$(printf \\n%s X) # $nr contiene "a capo" seguito da X
nr=$(printf \\n%s X) # $nr contiene "a capo" seguito da X
nr=${nr%X}          # $nr contiene "a capo" (la X è rimossa)
nr=${nr%X}          # $nr contiene "a capo" (la X è rimossa)
Riga 694: Riga 710:


==Espansione di percorso==
==Espansione di percorso==
{{Box | File | Su UNIX e Unix-like per file si può intendere sia un file regolare, ma anche una directory, un link simbolico, una pipe, un socket, un device, ecc...}}
{{Box | File | Su UNIX e Unix-like per file si può intendere sia un file regolare, ma anche una directory, un link simbolico, una pipe, un socket, un device, ecc...
 
L'espansione di percorso avviene unicamente in base al nome del file, che dev'essere univoco in una cartella, e non al tipo di file. L'unica eccezione sono le directory, perché possono essere identificate anche con lo slash (/) finale.}}


Le espansioni di percorso sono possibili solo se i caratteri speciali che la consentono (<code>* ? [ ]</code>) non sono racchiusi tra virgolette, apici o preceduti da <code>/</code>. È sempre consigliabile racchiudere tutto il resto tra virgolette, per non permettere espansioni accidentali.
Le espansioni di percorso sono possibili solo se i caratteri speciali che la consentono (<code>* ? [ ]</code>) non sono racchiusi tra virgolette, apici o preceduti da <code>/</code>. È sempre consigliabile racchiudere tutto il resto tra virgolette, per non permettere espansioni accidentali.
Riga 708: Riga 726:


Sintassi (prefisso e suffisso possono essere omessi, o essere variabili da espandere):
Sintassi (prefisso e suffisso possono essere omessi, o essere variabili da espandere):
* <code>prefisso'''?'''suffisso</code> sostituisce un singolo carattere di un file, con tutti quelli possibili che combaciano con le due stringhe date, ma di default tranne il '''.''' iniziale se manca il prefisso;
* <code>prefisso'''?'''suffisso</code> sostituisce un singolo carattere di un nome di file, con tutti quelli possibili che combaciano con le due stringhe date, ma di default tranne il '''.''' iniziale se manca il prefisso;
* <code>prefisso'''*'''suffisso</code> può sostituire tutti i caratteri di un file, ma di default tranne quelli nascosti se manca il prefisso (ossia quelli inizianti con '''.''').
* <code>prefisso'''*'''suffisso</code> può sostituire tutti i caratteri nei nomi di file, ma di default tranne quelli nascosti se manca il prefisso (ossia quelli inizianti con '''.''');
* <code>prefisso'''[classe]'''suffisso</code> sostituisce un singolo carattere di un nome di file, con tutti quelli possibili appartenenti alla classe data e che combaciano con le due stringhe. La classe può contenere:
**una lista di caratteri, tutti attaccati, per sostituirne uno qualsiasi della lista;
**un intervallo composto da due caratteri separati da un trattino <code>-</code>, per sostituirne uno qualsiasi dell'intervallo in base al loro valore ASCII;
**un <code>!</code> iniziale per farne il complemento, ossia sostituirne uno qualsiasi non presente nella classe;
**il carattere <code>!</code> può essere contenuto per il suo valore letterale in una classe purché non in prima posizione, mentre <code>-</code> è considerato letteralmente soltanto in prima posizione (o seconda se dopo <code>!</code>) e in ultima posizione.
 
Se un nome di file non include il percorso assoluto (iniziante con la directory radice '''/''') o relativo (iniziante con '''./''' oppure '''../''', dalla directory corrente e da quella superiore rispettivamente), di default si assume che sia nella directory corrente. Tuttavia per evitare ambiguità con i nomi delle opzioni di alcuni comandi, in presenza di possibili nomi di file inizianti con il trattino '''-''', in particolare se la parte iniziale del file è generata dall'espansione di percorso, è sempre bene rendere esplicito il percorso relativo premettendo '''./''' al nome del file.


Esempi (nella directory corrente):
Esempi (nella directory corrente):
* <code>file.???</code> si espande a tutti i file con nome "file" e con una qualsiasi estensione di tre caratteri;
* <code>./file.???</code> si espande a tutti i file con nome "file" e con una qualsiasi estensione di tre caratteri;
* <code>*</code> da solo espande a tutti i file non nascosti nella directory corrente. È sempre buona norma far precedere l'asterisco da un ./, che indica la cartella corrente, se non c'è un percorso assoluto o con tilda, per impedire espansioni di file inizianti con "-", che potrebbero essere visti come opzioni da alcuni comandi;
* <code>./???.ext</code> si espande a tutti i file con nomi di tre caratteri (salvo i file nascosti, ossia con '''.''' iniziale) ed estensione ext;
* <code>./*</code> equivalente a <code>*</code> ma più sicuro; di seguito si userà per tutte le espansioni (riguardanti la directory corrente);
* <code>./*</code> si espande a tutti i file non nascosti nella directory corrente;
* <code>./*.txt</code> espande a tutti i file con estensione .txt;
* <code>./*.txt</code> espande a tutti i file con estensione .txt ('''NOTA:''' anche directory e qualsiasi file non regolare avente tale estensione);
* <code>./*."${estensione}"</code> espande dopo aver espanso la variabile (contrariamente a ~), che può anche essere quotata;
* <code>./*."${estensione}"</code> espande dopo aver espanso la variabile (contrariamente a ~), che può anche essere quotata;
* <code>"./${nome}"*</code> espande a tutti i file inizianti con $nome;
* <code>"./${nome}"*</code> espande a tutti i file inizianti con ${nome};
* <code>./*/</code> espande a tutte le directory non nascoste;
* <code>./*/</code> espande a tutte le directory non nascoste;
* <code>./.*</code> espande a tutti i file nascosti ('''ATTENZIONE:''' comprese "'''.'''" e "'''..'''", ossia directory corrente e superiore).
* <code>./.*</code> espande a tutti i file nascosti ('''ATTENZIONE:''' comprese "'''.'''" e "'''..'''", ossia directory corrente e superiore);
* <code>[a-zA-Z]*</code> espande a tutti i file inizianti con una lettera qualsiasi (nessun file nascosto perché il punto non è nella classe);
* <code>./*.[tT][xX][tT]</code> espande a tutti i file con estensione txt (ignorando maiuscole e minuscole);
* <code>./.[!.]*</code> espande a tutti i file nascosti di almeno due caratteri in cui il secondo non è un punto (non espande a '''.''' e '''..''', ma nemmeno a possibili file nascosti inizianti con '''..''');
* <code>./..?*</code> espande a tutti i file nascosti di almeno tre caratteri in cui il secondo non è un punto (tutti i file nascosti saltati dal precedente, ma sempre escludendo '''.''' e '''..''');
* <code>./.[!.]* ./..?*</code> espande a tutti i file nascosti, esclusi '''.''' e '''..''' (''POSIX'').


È importante sapere che, se nessun file combacia con un dato pattern, allora l'espansione '''non''' viene effettuata e i caratteri mantengono il loro valore letterale. E inoltre <code>*</code> e <code>?</code> sono caratteri validi per un nome di file.
È importante sapere che, se nessun file combacia con un dato pattern, allora l'espansione '''non''' viene effettuata e i caratteri mantengono il loro valore letterale. E inoltre <code>*</code>, <code>?</code>, <code>[</code> e <code>]</code> sono caratteri validi per un nome di file.


L'esistenza di file ottenuti da tali espansioni va pertanto sempre controllata, per esempio con il costrutto <code>[ -e "$file" ]</code>:
L'esistenza di file ottenuti da tali espansioni va pertanto sempre controllata, impiegando l'espansione per generare una lista di stringhe all'interno di un ciclo <code>for</code>, ed effettuando poi il controllo di esistenza su ognuna:
<pre>for file in ./*; do
<pre>for file in ./*; do
   if [ -e "$file" ]; then
   if [ -e "$file" ]; then
Riga 729: Riga 759:
   fi
   fi
done</pre>
done</pre>
Si faccia attenzione che tutti i pattern delle espansioni di percorso, e non solo quelli composti da <code>*</code>, possono generare più stringhe, anche se sostituiscono un singolo carattere in presenza di fili multipli con lo stesso prefisso e/o suffisso (per esempio il pattern ''a?c'' può espandersi alla lista ''abc aBC acc'', se esistono questi tre file).


In alternativa, il comportamento di default dell'espansione può essere cambiato (in Bash), tramite <code>shopt</code>:
In alternativa il comportamento di default dell'espansione può essere cambiato in bash (non ''POSIX''), tramite <code>shopt -s</code>:
* '''dotglob''' espande ai file nascosti;
* '''nullglob''' espande a "niente" se non trova nessun file con un dato pattern, rendendo superfluo il controllo sull'esistenza;
* '''nullglob''' espande a "niente" se non trova nessun file con un dato pattern, rendendo superfluo il controllo sull'esistenza.
* '''dotglob''' espande ai file nascosti (ma non a '''.''' e '''..''');
Per esempio:
* '''nocaseglob''' espande il percorso a tutte le corrispondenze trovate, ignorando maiuscole e minuscole (''case-insensitive'').
Per esempio per espandere a tutti i file, compresi quelli nascosti:
<pre>
<pre>
shopt -s dotglob nullglob
shopt -s dotglob nullglob
Riga 741: Riga 773:
</pre>
</pre>


===Esempio: cambiare l'estensione ai file ===
===Esempio: cambiare l'estensione ai file regolari===
Rinomina tutti i file <code>*.txt</code> della directory corrente in <code>*.log</code>:
Rinomina tutti i file regolari <code>*.txt</code> della directory corrente in <code>*.log</code>, tramite il comando esterno <code>mv</code>:


<pre>
<pre>
for f in ./*.txt; do
for f in ./*.txt; do
   if [ -e "$f" ]; then
   if [ -f "$f" ]; then
       mv -- "$f" "${f%txt}log"
       mv -- "$f" "${f%txt}log"
   fi
   fi
done
done
</pre>
</pre>
Si noti che utilizzando <code>[ '''-f''' ... ]</code> in luogo di <code>[ '''-e''' ... ]</code>, si saltano anche tutti i file che non sono regolari, e che potrebbero essere restituiti dall'espansione di percorso.


==Espansione di parentesi (graffa)==
==Espansione di parentesi (graffa)==
Se i caratteri <code>{</code> e <code>}</code> non sono quotati, e non sono preceduti dal carattere di escape <code>\</code>, possono essere espansi con due diverse sintassi per generare una lista di stringhe. E più espansioni di parentesi possono essere annidate.
In '''bash''' (non ''POSIX'') se i caratteri <code>{</code> e <code>}</code> non sono quotati, e non sono preceduti dal carattere di escape <code>\</code>, possono essere espansi con due diverse sintassi per generare una lista di stringhe. E più espansioni di parentesi possono essere annidate.


Questa espansione avviene prima di tutte le altre, e il risultato può passare per tutte le altre espansioni. Non può avvenire in un'assegnazione, se non all'interno di altre espansioni.
Questa espansione avviene prima di tutte le altre, e il risultato può passare per tutte le altre espansioni. Non può avvenire in un'assegnazione, se non all'interno di altre espansioni.
Riga 793: Riga 827:


=Istruzioni composte=
=Istruzioni composte=
Rientrano in questa sezione tutte le istruzioni composte da una o più istruzioni più semplici.
Sono istruzioni composte tutte le istruzioni che possono contenere al loro interno altre istruzioni. Per esempio <code>if</code> e <code>for</code>, già trattate in precedenza per la loro importanza, sono due istruzioni composte.
 
==Cicli==
In aggiunta a <code>for</code>, trattato per le assegnazioni e che può essere considerato equivalente al ''for each'' di alcuni linguaggi di programmazione, con <code>while</code> è possibile eseguire un blocco di comandi se un comando ha successo e ripeterne l'esecuzione fintanto che il comando continua ad avere successo. Proprio come <code>if</code>, <code>while</code> è un'istruzione usata spesso in congiunzione con <code>[...]</code> per ripetere un blocco di codice tutte le volte che la condizione espressa è vera.
 
Sintassi base (in congiunzione con <code>[...]</code>):
<pre>
while [ espressione-booleana ]
do
  ...
done
</pre>


==Redirezione==
Per esempio:
Alcune comuni redirezioni (la lista non è esaustiva), da scriversi dopo un comando:
<pre>
* '''> file''' dopo un comando ne scrive l'output sul file (troncandolo, se esiste);
num=
* '''>> file''' aggiunge al file (creandolo, se non esiste);
# ripeti finché non è soddisfatta la condizione (si noti il ! davanti a [...])
* '''< file''' legge l'input dal file;
while ! [ "$num" -gt 0 ] 2> /dev/null
* '''>&2''' scrive l'output sullo standard error;
do
* '''2> file''' scrive lo standard error sul file (per aggiungere al file: '''2>>''');
  printf %s "Inserisci intero > 0 e premi INVIO: "
* '''2>&1''' scrive lo standard error sullo standard output;
  # assegna l'input dell'utente a $num
* '''&> file''' invia standard output ed error sul file (per aggiungere: '''&>>''');
  read num
* ''' | ''' : pipe che invia l'output del comando precedente al successivo. Tutti i comandi di una serie di pipe (anche l'ultimo) sono eseguiti in una subshell, e possono stare su righe diverse con la pipe come ultimo carattere.
done
printf %s\\n "Hai inserito: ${num}"
</pre>
 
Oppure al posto di <code>[...]</code> si può utilizzare un comando qualsiasi, per esempio per eseguire un ciclo infinito:
<pre>
while :
do
  ...
done
</pre>
 
Un ciclo può essere sempre:
* interrotto da <code>break</code>;
* ripreso dalla prima istruzione del blocco, dopo aver rivalutato l'exit status del comando dopo <code>while</code>, con <code>continue</code>.
 
==Composizione di comandi con pipe==
Una ''pipe'' collega due comandi, trattando l'output generato dal primo come input per il secondo, in modo da svolgere funzioni più complesse tramite la composizione comandi che si occupano di funzioni più limitate. Ed è una delle caratteristiche di ''UNIX'' che ogni comando debba svolgere un'unica funzione nel modo migliore possibile, permettendo la comunicazione con l'utente o un altro comando attraverso un flusso di testo, considerato un'interfaccia universale.
 
L'exit status di una serie di pipe è dato dall'ultimo comando.
 
Sintassi:
* comando1 '''|''' comando2: pipe che invia lo standard output del comando precedente al successivo. Tutti i comandi di una serie di pipe (anche l'ultimo) sono eseguiti in una subshell, e possono stare su righe diverse con la pipe come ultimo carattere;
* comando1 '''2>&1 |''' comando2: pipe che invia standard output e standard error (si veda la parte sui file descriptor) del comando precedente al successivo. Tutti i comandi di una serie di pipe (anche l'ultimo) sono eseguiti in una subshell, e possono stare su righe diverse con la pipe come ultimo carattere.
 
I più comuni comandi esterni impiegati con una pipe (per informazioni sulle loro opzioni si lascia alla lettura della loro pagina di manuale):
* <code>cat</code> per concatenare più file;
* <code>head</code> e <code>tail</code> per leggere la parte iniziale e finale di un file;
* <code>grep</code> per filtrare le righe di un file in base a un dato pattern;
* <code>sed</code> per modificare le parti corrispondenti a un dato pattern con altre stringhe;
* <code>cut</code> per selezionare dei campi da ogni riga, ma in base a un determinato carattere delimitatore (di default la tabulazione), conteggiato anche se ripetuto;
* <code>awk</code> per selezionare dei campi da ogni riga, separandoli in base a spazi e tabulazioni (ignorando quelli consecutivi). Questa è la sua funzione base, ma <code>awk</code> è un vero e proprio linguaggio di programmazione, che se padroneggiato può sostituire le funzionalità di buona parte degli altri comandi;
* <code>sort</code> e <code>uniq</code> per ordinare le righe in base a determinati campi e saltare quelle doppie;
* <code>wc</code> per conteggiare il numero di caratteri, parole e/o linee;
* <code>tr</code> per rimuovere o trasformare i caratteri di una data classe.


Esempi:
Per esempio:
<pre>
<pre>
comando &> /dev/null    # non stampa niente a schermo, neanche gli errori
# filtra le occorrenze di video nel ring buffer del kernel
comando > /dev/null 2>&1 # equivalente (POSIX)
dmesg | grep -i video    # -i per ignore-case (ignora maiuscole/minuscole)


# filtra le occorrenze di video
# stampa il gateway utilizzato
dmesg | grep -i video
ip route |              # stampa la tabella di routing
    grep "^default" |   # filtra solo la riga iniziante con default
    awk "{ print \$3 }" # stampa il terzo campo ($3 non dev'essere espanso dalla shell)
</pre>
</pre>


===Output dei comandi e carattere ASCII n. 0===
===Output dei comandi e carattere ASCII n. 0===
Una variabile non può contenere il carattere ASCII n. 0, che è usato per indicare la fine della stringa. Non potendo gestire direttamente il carattere ASCII n. 0, questo non può essere presente nell'espansione di un comando, ma dev'essere lasciato a comandi esterni mediante l'uso di una o più pipe.
Una variabile non può contenere il carattere ASCII n. 0, che è usato per indicare la fine della stringa. Non potendo gestire direttamente il carattere ASCII n. 0, questo non può essere presente nell'espansione di un comando, ma dev'essere riservato ai soli comandi mediante l'uso di una o più pipe.


Infatti non esiste un modo di contenere il carattere ASCII n. 0 in nessuna posizione:
Infatti non esiste un modo di contenere il carattere ASCII n. 0 in nessuna posizione:
<pre>
<pre>
var=$'\000'   # SBAGLIATO: $var è vuota
var=$'\000'               # SBAGLIATO: $var è vuota
var=$'X\000X' # SBAGLIATO: $var contiene XX
var=$'X\000X'             # SBAGLIATO: $var contiene X (solo la prima!)
var=$(printf %s\000%s X X) # SBAGLIATO: $var contiene XX
</pre>
</pre>
Si noti che mentre è rimosso dalle espansioni, nelle assegnazioni normali viene interpretato come carattere di fine stringa, con il risultato che anche tutto ciò che segue viene tralasciato.


====Nomi di file con caratteri non comuni====
Questo carattere è utile perché nemmeno i file possono averlo nel proprio nome, mentre invece permettono caratteri jolly (*, ?, ...), come già visto con le espansioni di percorso, e potrebbero contenere perfino il carattere "a capo".
Questo carattere è utile perché nemmeno i file possono averlo nel proprio nome, mentre invece permettono caratteri jolly (*, ?, ...), come già visto con le espansioni di percorso, e potrebbero contenere perfino il carattere "a capo".


Riga 840: Riga 924:
printf %s\\n "La directory prova contiene ${num_file} file" # restituisce 2 invece di 1
printf %s\\n "La directory prova contiene ${num_file} file" # restituisce 2 invece di 1


# forma corretta
# forma corretta (ma non la più efficiente, è solo per esemplificare l'uso del carattere)
num_file=$(find ./prova -type f -print0 | # stampo il carattere ASCII n. 0 dopo ogni file
num_file=$(find ./prova -type f -print0 | # stampo il carattere ASCII n. 0 dopo ogni file
  tr -dc '\000' |                # rimuovo tutti i caratteri ASCII diversi dal n. 0
  tr -dc '\000' |                # rimuovo tutti i caratteri ASCII diversi dal n. 0
Riga 852: Riga 936:
</pre>
</pre>


====Comando find e carattere ASCII n. 0====
Un abbinamento comune al comando esterno GNU <code>find</code> (dotato dell'opzione -print0) è il comando esterno GNU <code>xargs</code> (dotato dell'opzione -0):
Un abbinamento comune al comando esterno GNU <code>find</code> (dotato dell'opzione -print0) è il comando esterno GNU <code>xargs</code> (dotato dell'opzione -0):
<pre>
<pre>
Riga 858: Riga 943:
</pre>
</pre>
Si leggano i rispettivi manuali per maggiori informazioni. <code>find</code> ha la possibilità di eseguire altri comandi esterni sui file trovati direttamente con le opzioni -exec ed -execdir, ma la sintassi è più complessa e non supporta più di un processo per volta, come invece <code>xargs</code>.
Si leggano i rispettivi manuali per maggiori informazioni. <code>find</code> ha la possibilità di eseguire altri comandi esterni sui file trovati direttamente con le opzioni -exec ed -execdir, ma la sintassi è più complessa e non supporta più di un processo per volta, come invece <code>xargs</code>.
Con '''bash''' (non ''POSIX'') esiste un metodo per trattare indirettamente il carattere ASCII n. 0 anche con i comandi interni della shell, tramite l'istruzione <code>read</code> e l'opzione per impostare il carattere come delimitatore, così da replicare la funzionalità di <code>xargs -0</code>. In questo modo il carattere non verrà assegnato alle variabili, ma sarà usato all'interno di un ciclo per delimitare ogni valore dal successivo, permettendo la lettura dell'output generato da <code>find</code> (con <code>-print0</code>):
<pre>
find /percorso -opzione1 ... -opzioneN -print0 |
    while read -d $'\000' file
    do
        # posso usare "$file"
        ...
    done
</pre>
Si ricorda però che tutto il blocco <code>while</code>, facendo parte di una pipe, è eseguito in una subshell. E che quindi tutte le variabili sono reimpostate al loro valore precedente al termine del blocco.
È invece '''sempre sbagliato''', come soluzione generale se i nomi di file non seguono convenzioni note a priori, utilizzare l'espansione di comando con <code>find</code> all'interno di un ciclo <code>for</code>:
<pre>
for file in $(find ...) # NO!
</pre>


==Concatenazione e blocchi di comandi==
==Concatenazione e blocchi di comandi==
Riga 903: Riga 1 004:
status=$?  # se sbagliato
status=$?  # se sbagliato
</pre>
</pre>
==Funzioni==
Una funzione è un raggruppamento di comandi eseguito ogni volta che il nome della funzione è utilizzato come un comando. Per convenzione le definizioni delle funzioni precedono il corpo principale dello script.
All'interno del blocco di comandi (il corpo della funzione), le variabili speciali relative agli argomenti passati allo script (<code>$# $1 $2 ... $* $@</code>) fanno riferimento a quelli passati alla funzione.
Sintassi della definizione:
<pre>
nomefunzione () {
  ...
}
</pre>
I comandi contenuti nel corpo della funzione sono eseguiti ogni volta che nomefunzione (una singola stringa senza spazi con le stesse limitazioni dei nomi delle variabili) è scritto come comando. Si noti che <code>()</code> dopo il nome della funzione serve a identificare l'istruzione composta come una definizione di funzione, e non accetta argomenti al suo interno come altri linguaggi di programmazione, ma resta sempre <code>()</code> invariata.
In '''bash''' è possibile premettere <code>function</code> al nome della funzione, anche senza bisogno di <code>()</code>, ma questa possibilità non è prevista da ''POSIX''.
Nel corpo di una funzione è possibile utilizzare il modificatore <code>local</code> prima di un'assegnazione per rendere la variabile locale, ossia non accessibile fuori dal corpo della funzione. È bene evitare le variabili globali il più possibile (se non sono costanti), ricorrendo ai passaggi di parametri, per evitare la modifica accidentale di variabili esistenti fuori dalla funzione. Questo modificatore non è previsto da ''POSIX'', dove ogni variabile è globale, ma è comunque supportato da ogni moderna shell compatibile.
Il valore di ritorno di una funzione è quello dell'ultimo blocco eseguito. In alternativa è possibile specificarlo all'interno dei blocchi direttamente con la parola riservata <code>return</code> seguito dall'exit status (un valore qualsiasi tra 0, l'unico per "successo", e 255), facendo terminare la funzione immediatamente.
Se la funzione deve restituire un valore diverso da successo/insuccesso (o comunque per cui basterebbe l'exit status), è possibile:
* stamparlo come output, così che sia assegnabile a una variabile con l'espansione di comando, ma soltanto se non contiene caratteri non ammissibili (il carattere ASCII n. 0 e possibili "a capo" finali, che verrebbero rimossi). Si noti che questo metodo '''non''' è possibile in generale per i nomi di file, a meno che non seguano convenzioni note a priori;
* stamparlo come output seguito da un carattere qualsiasi (per esempio X), così che sia assegnabile a una variabile con l'espansione di comando, rimuovendo in seguito il carattere finale in più. Con questo metodo è possibile passare nomi di file qualsiasi, ma al solito non il carattere ASCII n. 0;
* utilizzare una variabile globale, eventualmente con il nome della funzione per evitare doppioni (per esempio: return_nomefunzione), contenente il valore da restituire nella funzione. Si noti che anche in questo caso il carattere ASCII n. 0 non può essere assegnato.
{{Warningbox | Esiste anche la possibilità di ricorrere a <code>eval</code>, ma il suo uso non è trattato in questa guida per la sua potenziale '''pericolosità''': possibile <code>code injection</code> e <code>privilege escalation</code>, se l'input non è controllato (''sanitized''). Su '''bash''' è molto meglio ricorrere all'espansione di parametro per l'accesso indiretto, per accedere a una variabile il cui nome è contenuto in un'altra; e ad array associativi per gli altri casi.}}
=File descriptor=
Su UNIX e Unix-like ogni processo che non è avviato in background ha di default tre '''file descriptor''', nella forma di identificativi interi:
* 0 ('''stdin'''), lo ''standard input'', da cui si leggono gli input (di default quanto scritto dalla tastiera sul terminale associato);
* 1 ('''stdout'''), lo ''standard output'', a cui si inviano tutti i messaggi prodotti (di default sul terminale associato);
* 2 ('''stderr'''), lo ''standard error'', a cui si inviano tutti i messaggi di errore prodotti (di default sul terminale associato).
Lo standard input è chiuso per i processi avviati in background.
Si chiamano ''file descriptor'' perché sono identificativi che fanno riferimento a un file; e infatti perfino i dispositivi, anche virtuali come il terminale, sono considerati dei file. Questo permette la possibilità di associare in modo trasparente questi file descriptor standard anche a file qualsiasi, oltre che ad altri file descriptor, in modo da ridirigerne il contenuto.  E disporre di due diversi file descriptor per l'output prodotto da un comando e i messaggi di errore, permette di disabilitare anche solo uno dei due, o di salvarli su due file diversi.
==Redirezioni==
Alcune comuni redirezioni, da scriversi dopo un comando (la stringa ''file'' può essere anche una variabile quotata):
* '''< file''' collega lo standard input al file, in modo da leggerne il contenuto. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''0< file''';
* '''> file''' dopo un comando ne scrive lo standard output sul file (troncandolo, se esiste). Si noti che il file descriptor è implicito, ma sarebbe equivalente scrivere '''1> file''';
* '''>> file''' (''append'') aggiunge il contenuto dello standard output al file (creandolo, se non esiste). Il file descriptor è implicito, ma sarebbe equivalente scrivere '''1>> file''';
* '''2> file''' è equivalente a '''> file''', ma qui si specifica di ridirigere lo standard error anziché lo standard output sul file (con ''append'': '''2>> file''');
* '''>&2''' scrive lo standard output sullo standard error. Il file descriptor dello standard output è implicito, mentre quello alla destra dev'essere preceduto da <code>&</code>, ma sarebbe equivalente scrivere '''1>&2''';
* '''2>&1''' scrive lo standard error sullo standard output;
* '''&> file''' (non ''POSIX'', abbreviazione per: '''> file 2>&1''') invia standard output ed error sul file (con ''append'': '''&>> file''');
* '''<&-''' chiude lo standard input. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''0<&-''';
* '''>&-''' chiude lo standard output. Il file descriptor è implicito, ma sarebbe equivalente scrivere '''1>&-''';
* '''2>&-''' chiude lo standard error.
Per evitare la scrittura a schermo standard output e/o standard error non vanno chiusi, oppure l'operazione genererebbe un errore (perché il file descriptor relativo non esisterebbe più), ma ridiretti su <code>/dev/null</code>, un file dispositivo la cui esistenza è specificata da ''POSIX'' che non produce mai output e può essere usato per assorbire qualsiasi cosa.
Esempi:
<pre>
comando &> /dev/null    # non stampa niente a schermo, neanche gli errori
comando > /dev/null 2>&1 # equivalente (POSIX)
# si noti la differenza con:
comando >&- 2>&-        # standard output ed error sono chiusi, quindi se
                        # venissero usati, l'exit status sarebbe diverso da 0!
</pre>
Una pipe (<code>comando1 '''|''' comando2</code>), già trattata nella sezione sulle istruzioni composte, è anch'essa una forma di redirezione, in cui lo standard output di comando1 diventa lo standard input di comando2.
===Scope della redirezione===
Si noti che lo ''scope'' (il raggio d'azione) della redirezione non serve soltanto per evitare di scrivere più volte il percorso del file, ma ne influenza anche il significato. In particolare con la redirezione dello standard input (lettura), per garantire che tutte le istruzioni continuino la lettura da dove era rimasta invece che riprenderla sempre dall'inizio, e dello standard output, per garantire che il file non venga troncato da ogni comando ma che i successivi continuino a scrivere in seguito (come se usassero la redirezione con ''append'').
Per esempio in genere è '''sbagliato''' scrivere:
<pre>
while read riga < file
do
  # "$riga" contiene *SEMPRE* la prima riga del file
  # quindi il ciclo può essere eseguito per sempre se ha più di una riga!
done
</pre>
Mentre quello che si vuole è probabilmente:
<pre>
while read riga
do
    # "$riga" contiene sempre una nuova linea del file
    # il ciclo è eseguito per ogni linea del file
done < file
</pre>
Si noti che leggere in questo modo un file ha senso solo per file non binari, che potrebbero contenere anche il carattere ASCII n. 0, che non può essere memorizzato in una variabile. Inoltre l'istruzione <code>read</code> fallisce se non trova un "a capo", per cui l'ultima riga del file è letta all'interno del ciclo soltanto se termina con un "a capo", altrimenti è necessario controllare se è vuota la variabile dopo il ciclo.
==Nuovi file descriptor==
In aggiunta ai tre file descriptor standard, è possibile aprirne di nuovi, in lettura (<code>FD<...</code>...), in scrittura (<code>FD>...</code>, append: <code>FD>>...</code>) e in lettura/scrittura (<code>FD<>...</code>), dove FD è un intero rappresentante il nuovo file descriptor e al posto dei puntini può esserci sia un file sia un file descriptor già esistente preceduto dal carattere <code>&</code>.
È necessario quindi determinare a quale file (o file descriptor, se preceduto da <code>&</code>) il nuovo file descriptor va associato. Ci sono due modi:
* specificarlo dopo un'istruzione composta, in modo che il file descriptor sia utilizzabile normalmente all'interno;
* specificarlo con l'istruzione <code>exec</code> senza altri argomenti (o avrebbe un significato diverso).
Per usarlo invece, una volta aperto, il file descriptor va scritto subito dopo la redirezione, preceduto dal carattere <code>&</code>.
Per esempio:
<pre>
{
  # il file descriptor 3 è definito dentro il blocco (in scrittura)
  comando1 >&3  # ridirige lo standard output sul nuovo file descriptor
  comando2
  comando3 >&3  # come per comando1
} 3> file # apre in scrittura il file descriptor per il contenuto del blocco
# NOTA: fuori dal blocco il nuovo file descriptor non esiste più
</pre>
Sul file scelto sono ridiretti lo standard output di comando1 e comando3. È equivalente a:
<code>
comando1 > file  # scrittura
comando2
comando3 >> file # scrittura con append! (nel blocco è implicito)
</code>
Equivalentemente, con <code>exec</code>:
<pre>
exec 3> file    # apre il file descriptor in scrittura
comando1 >&3    # usa il file descriptor
comando2
comando3 >&3    # usa il file descriptor
exec 3>&-        # chiude il file descriptor
</pre>
Finché un file descriptor non è chiuso, non è garantita la scrittura sul file, ma la scrittura tramite file descriptor è più efficiente.
===Salvare i file descriptor===
Si noti che <code>exec</code> è equivalente a una redirezione in un blocco, ma la chiusura dei file descriptor aperti dev'essere gestita manualmente. Prima di cambiare quelli standard, per consentirne il successivo ripristino al loro valore originale, vanno salvati in un nuovo file descriptor temporaneo:
<pre>
exec 3<&0  # apre un nuovo file descriptor per salvare lo standard input
exec < file # sovrascrive lo standard input
read riga  # legge la prima riga (se esiste)
read riga  # legge la seconda riga (se esiste)! (e non di nuovo la prima)
exec <&3    # assegna nuovamente lo standard input al suo valore precedente
exec 3<&-  # chiude il nuovo file descriptor
</pre>
si ricordi che invece, senza exec e senza un blocco, una redirezione (di lettura o scrittura senza append) su un singolo comando parte sempre dall'inizio:
<pre>
read riga < file # legge la prima riga del file (se esiste)
read riga < file # legge sempre la prima riga del file (se esiste)!
</pre>
=Segnali=
Ogni processo in UNIX e Unix-like può ricevere dei segnali da altri processi (se appartengono allo stesso utente oppure da root). Di seguito i principali, tra quelli previsti da ''POSIX'':
* '''INT''' : è equivalente a <code>Ctrl-c</code> da tastiera, per uno script associato a un terminale (non eseguito in background). L'azione di default è la terminazione;
* '''QUIT''' : è equivalente a <code>Ctrl-\</code> da tastiera, per uno script associato a un terminale (non eseguito in background). L'azione di default è la terminazione con core dump;
* '''HUP''' : è equivalente a chiudere il terminale, per uno script associato a un terminale (anche se eseguito in background), o in generale alla morte di un processo padre. L'azione di default è la terminazione;
* '''TERM''' : è il segnale di terminazione di default. L'azione di default è la terminazione;
* '''KILL''' : è un segnale di terminazione che non può essere ignorato (si veda trap). L'unica azione che sarà eseguita è la sua terminazione immediata;
* '''PIPE''' : è equivalente al segnale ricevuto da un processo che invia l'output a una pipe, quando il processo lettore ha terminato. L'azione di default è la terminazione;
* '''ABRT''' : è il segnale di ''abort''. L'azione di default è la terminazione con core dump;
* '''STOP''' : è equivalente a <code>Ctrl-z</code> da tastiera, per uno script associato a un terminale (non eseguito in background), ma come KILL non può mai essere ignorato (si veda trap). L'unica azione che sarà eseguita è la sua interruzione immediata;
* '''CONT''' : è il segnale per continuare l'esecuzione per un processo interrotto. Se il processo non è interrotto, di default il segnale è ignorato;
* '''TSTP''' : è equivalente a Bloc Scor (o <code>Ctrl-s</code>; per riprendere: <code>Ctrl-q</code>), per bloccare l'output. L'azione di default è interrompere il processo;
* '''CHLD''' : è il segnale che un processo figlio è terminato (viene inviato automaticamente). Di default è ignorato;
* '''WINCH''' : è il segnale che la finestra del terminale è stata ridimensionata. Di default è ignorato;
* '''USR1''' : è un segnale lasciato da definire all'utente. L'azione di default è la terminazione;
* '''USR2''' : è un segnale lasciato da definire all'utente. L'azione di default è la terminazione.
Il ''core dump'' è un dump dello stato (o una sua parte) del processo terminato, a scopo di debug. Può essere limitato e anche disattivato interamente. Se non attivo, l'azione eseguita è equivalente a una terminazione senza core dump.
==Invio e cattura di segnali==
Alcuni segnali vengono inviati automaticamente, al sussistere di determinate condizioni, ma possono anche essere inviati esplicitamente con il comando <code>kill</code>.<br/>
Sintassi: <code>kill [ -s SIGNAL ] pid</code><br/>
Invia un segnale (di default: TERM) al processo con il pid scelto. Si noti che l'invio è asincrono, ossia <code>kill</code> non resta in attesa che il processo con pid scelto effettui l'azione associata al segnale. Di conseguenza il comando ha sempre successo, salvo che il pid non esista o non possa ricevere il segnale (per esempio perché non è dello stesso utente).
Le azioni da intraprendere possono essere modificati con l'istruzione <code>trap</code>.<br/>
Sintassi: <code>trap 'stringa' SIGNAL1 [ ... ]</code><br/>
Cattura i segnali (se possibile, non lo è per KILL e STOP), con il seguente comportamento:
* se la stringa è il solo carattere <code>-</code>, le azioni associate alla lista di segnali sono impostate nuovamente come quelle di default per i segnali;
* se la stringa è vuota, i segnali verranno ignorati;
* se la stringa è il nome di una funzione, verrà eseguita ogni volta che uno dei segnali viene ricevuto. L'azione normale è sovrascritta, quindi se è necessario preservarla dopo la funzione, dovrà occuparsene la funzione stessa (per esempio di uscire con <code>exit</code> dopo aver effettuato la pulizia);
* il segnale può essere '''EXIT''', che ha significato solo per questa istruzione, in tal caso la funzione sarà eseguita alla fine dello script, che può essere causata anche da segnali (con l'unica eccezione di KILL), purché lo script non termini con un <code>exec</code> seguito da un eseguibile.
{{Warningbox | Al posto di 'stringa' nell'istruzione <code>trap</code> può esserci un qualsiasi comando, ma si consiglia l'uso di una funzione sia per chiarezza del codice, sia soprattutto per evitare la doppia interpretazione con possibili espansioni in fase di definizione di <code>trap</code> prima e di esecuzione del comando poi. Un uso diverso ha la stessa pericolosità di <code>eval</code>, e può portare a <code>code injection</code> e <code>privilege escalation</code>.}}
Un esempio classico di <code>trap</code>:
<pre>
clean () {
    # funzione di pulizia
    ...
}
trap 'clean' EXIT  # la funzione clean è eseguita se si riceve un segnale di terminazione
...
if comando; then
    exec eseguibile # clean NON è eseguita dopo exec e un eseguibile
fi
...
exit 0              # clean è eseguita anche con exit
</pre>
In caso di esecuzione di una subshell le azioni da intraprendere in seguito alla ricezione di un segnale vengono reimpostate al loro valore di default, ma i segnali ignorati continuano a restare ignorati.


=Link=
=Link=
* [http://www.gnu.org/software/bash/manual/bash.html Bash Referece Manual]: manuale ufficiale
* [http://www.gnu.org/software/bash/manual/bash.html Bash Referece Manual]: manuale ufficiale
* [http://www.tldp.org/LDP/abs/html/ Advanced Bash-Scripting Guide]: la '''Bibbia''' dello bash scripting.
* [http://www.tldp.org/LDP/abs/html/ Advanced Bash-Scripting Guide]: la '''Bibbia''' dello bash scripting.
Riga 913: Riga 1 203:
|Verificata_da=
|Verificata_da=
:[[Utente:S3v|S3v]] (in Bash tips)
:[[Utente:S3v|S3v]] (in Bash tips)
:[[Utente:HAL 9000|HAL 9000]] 13:24, 16 lug 2014 (CEST)
:[[Utente:HAL 9000|HAL 9000]] 18:15, 21 lug 2014 (CEST)
|Estesa_da=
|Estesa_da=
:[[Utente:S3v|S3v]] (in Bash tips)
:[[Utente:S3v|S3v]] (in Bash tips)