Guida avanzata di scripting Bash: Un'approfondita esplorazione dell'arte dello scripting di shell | ||
---|---|---|
Indietro | Avanti |
Un builtin è un comando appartenente alla serie degli strumenti Bash, letteralmente incorporato. Questo è stato fatto sia per motivi di efficienza -- i builtin eseguono più rapidamente il loro compito di quanto non facciano i comandi esterni, che di solito devono generare un processo separato (forking) -- sia perché particolari builtin necessitano di un accesso diretto alle parti interne della shell.
Quando un comando, o la stessa shell, svolge un certo compito, dà origine (spawn) ad un nuovo sottoprocesso. Questa azione si chiama forking. Il nuovo processo è il figlio, mentre il processo che l'ha generato è il genitore. Mentre il processo figlio sta svolgendo il proprio lavoro, il processo genitore resta ancora in esecuzione. Si noti che mentre un processo genitore ottiene l'ID di processo del processo figlio, riuscendo in questo modo a passargli degli argomenti, non è vero l'inverso. Ciò può creare dei problemi che sono subdoli e difficili da individuare. Esempio 14-1. Uno script che genera istanze multiple di sé stesso
In genere, un builtin Bash eseguito in uno script non genera un sottoprocesso. Al contrario, un filtro o un comando di sistema esterno, solitamente, avvia (fork) un sottoprocesso. |
Un builtin può avere un nome identico a quello di un comando di sistema. In questo caso Bash lo reimplementa internamente. Per esempio, il comando Bash echo non è uguale a /bin/echo, sebbene la loro azione sia quasi identica.
#!/bin/bash echo "Questa riga usa il builtin \"echo\"." /bin/echo "Questa riga usa il comando di sistema /bin/echo." |
Una parola chiave è un simbolo, un operatore o una parola riservata. Le parole chiave hanno un significato particolare per la shell e, infatti, rappresentano le componenti strutturali della sua sintassi . Ad esempio "for", "while", "do" e "!" sono parole chiave. Come un builtin, una parola chiave è una componente interna di Bash, ma a differenza di un builtin, non è di per se stessa un comando, ma parte di una struttura di comandi più ampia. [1]
visualizza (allo stdout) un'espressione o una variabile (vedi Esempio 4-1).
echo Ciao echo $a |
echo richiede l'opzione
-e
per visualizzare le sequenze di escape.
Vedi Esempio 5-2.
Normalmente, ogni comando echo
visualizza una nuova riga. L'opzione -n
annulla questo comportamento.
echo può essere utilizzato per fornire una sequenza di comandi in una pipe.
|
Si può utilizzare echo, in combinazione con la sostituzione di comando, per impostare una variabile. a=`echo "CIAO" | tr A-Z a-z` Vedi anche Esempio 15-19, Esempio 15-3, Esempio 15-42 ed Esempio 15-43. |
Si faccia attenzione che echo `comando` cancella tutti i ritorni a capo generati dall'output di comando.
La variabile $IFS (internal field separator), di norma, comprende \n (ritorno a capo) tra i suoi caratteri di spaziatura. Bash, quindi, scinde l'output di comando in corrispondenza dei ritorni a capo. Le parti vengono passate come argomenti a echo. Di conseguenza echo visualizza questi argomenti separati da spazi.
bash$ ls -l /usr/share/apps/kjezz/sounds -rw-r--r-- 1 root root 1407 Nov 7 2000 reflect.au -rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au bash$ echo `ls -l /usr/share/apps/kjezz/sounds` total 40 -rw-r--r-- 1 root root 716 Nov 7 2000 reflect.au -rw-r--r-- 1 root root 362 Nov 7 2000 seconds.au |
Quindi, in che modo si può inserire un "a capo" in una stringa di caratteri da visualizzare con echo?
# Incorporare un a capo? echo "Perché questa stringa non viene \n suddivisa su due righe?" # Non viene divisa. # Proviamo qualcos'altro. echo echo $"Riga di testo contenente un a capo." # Viene visualizzata su due righe distinte (a capo incorporato). # Ma, il prefisso di variabile "$" è proprio necessario? echo echo "Questa stringa è divisa su due righe." # No, il "$" non è necessario. echo echo "---------------" echo echo -n $"Un'altra riga di testo contenente un a capo." # Viene visualizzata su due righe (a capo incorporato). # In questo caso neanche l'opzione -n riesce a sopprimere l'a capo. echo echo echo "---------------" echo echo # Tuttavia, quello che segue non funziona come potremmo aspettarci. # Perché no? Suggerimento: assegnamento a una variabile. stringa1=$"Ancora un'altra riga di testo contenente un a capo (forse)." echo $stringa1 # Ancora un'altra riga di testo contenente un a_capo (forse). # ^ # L'a_capo è diventato uno spazio. # Grazie a Steve Parker per la precisazione. |
Questo comando è un builtin di shell e non è uguale a /bin/echo, sebbene la sua azione sia simile.
|
Il comando printf, visualizzazione formattata,
rappresenta un miglioramento di echo. È una variante
meno potente della funzione di libreria printf()
del linguaggio C.
Anche la sua sintassi è un po' differente.
printf stringa-di-formato... parametro...
È la versione builtin Bash del comando /bin/printf o /usr/bin/printf. Per una descrizione dettagliata, si veda la pagina di manuale di printf (comando di sistema).
Le versioni più vecchie di Bash potrebbero non supportare printf. |
Esempio 14-2. printf in azione
#!/bin/bash # printf demo PI=3,14159265358979 # Vedi nota a fine listato CostanteDecimale=31373 Messaggio1="Saluti," Messaggio2="un abitante della Terra." echo printf "Pi con 2 cifre decimali = %1.2f" $PI echo printf "Pi con 9 cifre decimali = %1.9f" $PI # Esegue anche il corretto #+ arrotondamento. printf "\n" # Esegue un ritorno a capo, # equivale a 'echo'. printf "Costante = \t%d\n" $CostanteDecimale # Inserisce un carattere #+ di tabulazione (\t) printf "%s %s \n" $Messaggio1 $Messaggio2 echo # ==================================================# # Simulazione della funzione sprintf del C. # Impostare una variabile con una stringa di formato. echo Pi12=$(printf "%1.12f" $PI) echo "Pi con 12 cifre decimali = $Pi12" Msg=`printf "%s %s \n" $Messaggio1 $Messaggio2` echo $Msg; echo $Msg # Ora possiamo disporre della funzione 'sprintf' come modulo #+ caricabile per Bash. Questo, però, non è portabile. exit 0 # N.d.T. Nella versione originale veniva usato il punto come separatore #+ decimale. Con le impostazioni locali italiane il punto avrebbe #+ impedito il corretto funzionamento di printf. |
Un'utile applicazione di printf è quella di impaginare i messaggi d'errore
E_ERR_DIR=65 var=directory_inesistente errore() { printf "$@" >&2 # Organizza i parametri posizionali passati e li invia allo stderr. echo exit $E_ERR_DIR } cd $var || errore $"Non riesco a cambiare in %s." "$var" # Grazie, S.C. |
"Legge" il valore
di una variabile dallo stdin, vale a
dire, preleva in modo interattivo l'input dalla tastiera.
L'opzione -a
permette a
read di assegnare le variabili di un
array (vedi Esempio 26-6).
Esempio 14-3. Assegnamento di variabile utilizzando read
#!/bin/bash # "Leggere" variabili. echo -n "Immetti il valore della variabile 'var1': " # L'opzione -n di echo sopprime il ritorno a capo. read var1 # Notate che non vi è nessun '$' davanti a var1, perché la variabile #+ è in fase di impostazione. echo "var1 = $var1" echo # Un singolo enunciato 'read' può impostare più variabili. echo -n "Immetti i valori delle variabili 'var2' e 'var3' (separati da \ uno spazio o da tab): " read var2 var3 echo "var2 = $var2 var3 = $var3" # Se si immette un solo valore, le rimanenti variabili restano non #+ impostate (nulle). exit 0 |
Se a read non è associata una variabile, l'input viene assegnato alla variabile dedicata $REPLY.
Esempio 14-4. Cosa succede quando a read non è associata una variabile
#!/bin/bash # read-novar.sh echo # -------------------------- # echo -n "Immetti un valore: " read var echo "\"var\" = "$var"" # Tutto come ci si aspetta. # -------------------------- # echo # ---------------------------------------------------------------- # echo -n "Immetti un altro valore: " read # Non viene fornita alcuna variabile a 'read', #+ quindi... l'input di 'read' viene assegnato alla #+ variabile predefinita $REPLY. var="$REPLY" echo "\"var\" = "$var"" # Stesso risultato del primo blocco di codice. # ---------------------------------------------------------------- # echo exit 0 # Questo esempio è simile allo script "reply.sh". # Mostra, però, che $REPLY è ancora disponibile #+ anche dopo un 'read' con una variabile specificata. |
Normalmente, immettendo una \
nell'input di read si disabilita il
ritorno a capo. L'opzione -r
consente di
interpretare la \ letteralmente.
Esempio 14-5. Input su più righe per read
#!/bin/bash echo echo "Immettete una stringa che termina con \\, quindi premete <INVIO>." echo "Dopo di che, immettete una seconda stringa e premete ancora <INVIO>." read var1 # La "\" sopprime il ritorno a capo durante la lettura di $var1. # prima riga \ # seconda riga echo "var1 = $var1" # var1 = prima riga seconda riga # Per ciascuna riga che termina con "\", si ottiene un prompt alla riga #+ successiva per continuare ad inserire caratteri in var1. echo; echo echo "Immettete un'altra stringa che termina con \\ , quindi premete <INVIO>." read -r var2 # L'opzione -r fa sì che "\" venga interpretata letteralmente. # prima riga \ echo "var2 = $var2" # var2 = prima riga \ # L'introduzione dei dati termina con il primo <INVIO>. echo exit 0 |
Il comando read possiede alcune interessanti opzioni che consentono di visualizzare un prompt e persino di leggere i tasti premuti senza il bisogno di premere INVIO.
# Rilevare la pressione di un tasto senza dover premere INVIO. read -s -n1 -p "Premi un tasto " tasto echo; echo "Hai premuto il tasto "\"$tasto\""." # L'opzione -s serve a non visualizzare l'input. # L'opzione -n N indica che devono essere accettati solo N caratteri di input. # L'opzione -p permette di visualizzare il messaggio del prompt immediatamente #+ successivo, prima di leggere l'input. # Usare queste opzioni è un po' complicato, perché #+ devono essere poste nell'ordine esatto. |
L'opzione -n
di read
consente anche il rilevamento dei tasti freccia
ed alcuni altri tasti inusuali.
Esempio 14-6. Rilevare i tasti freccia
#!/bin/bash # arrow-detect.sh: Rileva i tasti freccia, e qualcos'altro. # Grazie a Sandro Magi per avermelo mostrato. # -------------------------------------------------------- # Codice dei caratteri generati dalla pressione dei tasti. frecciasu='\[A' frecciagiù='\[B' frecciadestra='\[C' frecciasinistra='\[D' ins='\[2' canc='\[3' # -------------------------------------------------------- SUCCESSO=0 ALTRO=65 echo -n "Premi un tasto... " # Potrebbe essere necessario premere anche INVIO se viene premuto un #+ tasto non tra quelli elencati. read -n3 tasto # Legge 3 caratteri. echo -n "$tasto" | grep "$frecciasu" # Verifica il codice del #+ tasto premuto. if [ "$?" -eq $SUCCESSO ] then echo "È stato premuto il tasto Freccia-su." exit $SUCCESSO fi echo -n "$tasto" | grep "$frecciagiù" if [ "$?" -eq $SUCCESSO ] then echo "È stato premuto il tasto Freccia-giù." exit $SUCCESSO fi echo -n "$tasto" | grep "$frecciadestra" if [ "$?" -eq $SUCCESSO ] then echo "È stato premuto il tasto Freccia-destra." exit $SUCCESSO fi echo -n "$tasto" | grep "$frecciasinistra" if [ "$?" -eq $SUCCESSO ] then echo "È stato premuto il tasto Freccia-sinistra." exit $SUCCESSO fi echo -n "$tasto" | grep "$ins" if [ "$?" -eq $SUCCESSO ] then echo "È stato premuto il tasto \"Ins\"." exit $SUCCESSO fi echo -n "$tasto" | grep "$canc" if [ "$?" -eq $SUCCESSO ] then echo "È stato premuto il tasto \"Canc\"." exit $SUCCESSO fi echo " È stato premuto un altro tasto." exit $ALTRO # Esercizi: # -------- # 1) Semplificate lo script trasformando le verifiche multiple "if" in un # costrutto 'case'. # 2) Aggiungete il rilevamento dei tasti "Home", "Fine", "PgUp" e "PgDn". # N.d.T. Attenzione! I codici dei tasti indicati all'inizio potrebbero non #+ corrispondere a quelli della vostra tastiera. # Verificateli e quindi, modificate l'esercizio in modo che funzioni #+ correttamente. |
L'opzione |
L'opzione -t
di read
consente un input temporizzato (vedi Esempio 9-4).
Il comando read può anche "leggere" il valore da assegnare alla variabile da un file rediretto allo stdin. Se il file contiene più di una riga, solo la prima viene assegnata alla variabile. Se read ha più di un parametro, allora ad ognuna di queste variabili vengono assegnate le stringhe successive delimitate da spazi. Attenzione!
Esempio 14-7. Utilizzare read con la redirezione di file
#!/bin/bash read var1 <file-dati echo "var1 = $var1" # var1 viene impostata con l'intera prima riga del file di input "file-dati" read var2 var3 <file-dati echo "var2 = $var2 var3 = $var3" # Notate qui il comportamento poco intuitivo di "read". # 1) Ritorna all'inizio del file di input. # 2) Ciascuna variabile viene impostata alla stringa corrispondente, # separata da spazi, piuttosto che all'intera riga di testo. # 3) La variabile finale viene impostata alla parte rimanente della riga. # 4) Se ci sono più variabili da impostare di quante siano le # stringhe separate da spazi nella prima riga del file, allora le # variabili in eccesso restano vuote. echo "------------------------------------------------" # Come risolvere il problema precedente con un ciclo: while read riga do echo "$riga" done <file-dati # Grazie a Heiner Steven per la puntualizzazione. echo "------------------------------------------------" # Uso della variabile $IFS (Internal Field Separator) per suddividere #+ una riga di input per "read", #+ se non si vuole che il delimitatore preimpostato sia la spaziatura. echo "Elenco di tutti gli utenti:" OIFS=$IFS; IFS=: # /etc/passwd usa ":" come separatore di campo. while read name passwd uid gid fullname ignore do echo "$name ($fullname)" done <etc/passwd # Redirezione I/O. IFS=$OIFS # Ripristina il valore originario di $IFS. # Anche questo frammento di codice è di Heiner Steven. # Impostando la variabile $IFS all'interno dello stesso ciclo, #+ viene eliminata la necessità di salvare il valore originario #+ di $IFS in una variabile temporanea. # Grazie, Dim Segebart per la precisazione. echo "------------------------------------------------" echo "Elenco di tutti gli utenti:" while IFS=: read name passwd uid gid fullname ignore do echo "$name ($fullname)" done <etc/passwd # Redirezione I/O. echo echo "\$IFS è ancora $IFS" exit 0 |
Il tentativo di impostare delle variabili collegando con una pipe l'output del comando echo a read, fallisce. Tuttavia, collegare con una pipe l'output di cat sembra funzionare.
Comunque, come mostra Bjön Eriksson: Esempio 14-8. Problemi leggendo da una pipe
Lo script gendiff, che di solito si trova in /usr/bin in molte distribuzioni Linux, usa una pipe per collegare l'output di find ad un costrutto while read.
|
Il familiare comando di cambio di directory cd viene usato negli script in cui, per eseguire un certo comando, è necessario trovarsi in una directory specifica.
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -) |
L'opzione -P
(physical) di
cd permette di ignorare i link
simbolici.
cd - cambia a $OLDPWD, la directory di lavoro precedente.
Il comando cd non funziona come ci si potrebbe aspettare quando è seguito da una doppia barra.
|
Print Working Directory. Fornisce la directory corrente dell'utente (o dello script) (vedi Esempio 14-9). Ha lo stesso effetto della lettura del valore della variabile builtin $PWD.
Questa serie di comandi forma un sistema per tenere nota delle directory di lavoro; un mezzo per spostarsi avanti e indietro tra le directory in modo ordinato. Viene usato uno stack (del tipo LIFO) per tenere traccia dei nomi delle directory. Diverse opzioni consentono varie manipolazioni dello stack delle directory.
pushd nome-dir immette il percorso di nome-dir nello stack delle directory e simultaneamente passa dalla directory di lavoro corrente a nome-dir
popd preleva (pop) il nome ed il percorso della directory che si trova nella locazione più alta dello stack delle directory e contemporaneamente passa dalla directory di lavoro corrente a quella prelevata dallo stack.
dirs elenca il contenuto dello stack delle directory (lo si confronti con la variabile $DIRSTACK). Un comando pushd o popd, che ha avuto successo, invoca in modo automatico dirs.
Gli script che necessitano di ricorrenti cambiamenti
delle directory di lavoro possono trarre giovamento
dall'uso di questi comandi, evitando di dover codificare
ogni modifica all'interno dello script. È da notare
che nell'array implicito $DIRSTACK
,
accessibile da uno script, è memorizzato il
contenuto dello stack delle directory.
Esempio 14-9. Cambiare la directory di lavoro corrente
#!/bin/bash dir1=/usr/local dir2=/var/spool pushd $dir1 # Viene eseguito un 'dirs' automatico (visualizza lo stack delle #+ directory allo stdout). echo "Ora sei nella directory `pwd`." # Uso degli apici singoli #+ inversi per 'pwd'. # Ora si fa qualcosa nella directory 'dir1'. pushd $dir2 echo "Ora sei nella directory `pwd`." # Adesso si fa qualcos'altro nella directory 'dir2'. echo "Nella posizione più alta dell'array DIRSTACK si trova $DIRSTACK." popd echo "Sei ritornato alla directory `pwd`." # Ora si fa qualche altra cosa nella directory 'dir1'. popd echo "Sei tornato alla directory di lavoro originaria `pwd`." exit 0 # Cosa succede se non eseguite 'popd' -- prima di uscire dallo script? # In quale directory vi trovereste alla fine? Perché? |
Il comando let permette di eseguire le operazioni aritmetiche sulle variabili. In molti casi, opera come una versione meno complessa di expr.
Esempio 14-10. Facciamo fare a let qualche calcolo aritmetico.
#!/bin/bash echo let a=11 # Uguale a 'a=11' let a=a+5 # Equivale a let "a = a + 5" # (i doppi apici e gli spazi la rendono più leggibile.) echo "11 + 5 = $a" # 16 let "a <<= 3" # Equivale a let "a = a << 3" echo "\"\$a\" (=16) scorrimento a sinistra di 3 bit = $a" # 128 let "a /= 4" # Equivale a let "a = a / 4" echo "128 / 4 = $a" # 32 let "a -= 5" # Equivale a let "a = a - 5" echo "32 - 5 = $a" # 27 let "a = a * 10" # Equivale a let "a = a * 10" echo "27 * 10 = $a" # 270 let "a %= 8" # Equivale a let "a = a % 8" echo "270 modulo 8 = $a (270 / 8 = 33, resto $a)" # 6 echo exit 0 |
eval arg1 [arg2] ... [argN]
Combina gli argomenti presenti in un'espressione, o in una lista di espressioni, e li valuta. Espande qualsiasi variabile presente nell'espressione. Il risultato viene tradotto in un comando. Può essere utile per generare del codice da riga di comando o da uno script.
bash$ processo=xterm bash$ mostra_processo="eval ps ax | grep $processo" bash$ $mostra_processo 1867 tty1 S 0:02 xterm 2779 tty1 S 0:00 xterm 2886 pts/1 S 0:00 grep xterm |
Esempio 14-11. Dimostrazione degli effetti di eval
#!/bin/bash y=`eval ls -l` # Simile a y=`ls -l` echo $y #+ ma con i ritorni a capo tolti perché la variabile #+ "visualizzata" è senza "quoting". echo echo "$y" # I ritorni a capo vengono mantenuti con il #+ "quoting" della variabile. echo; echo y=`eval df` # Simile a y=`df` echo $y #+ ma senza ritorni a capo. # Se non si preservano i ritorni a capo, la verifica dell'output #+ con utility come "awk" risulta più facile. echo echo "=======================================================================" echo # Ora vediamo come "espandere" una variabile usando "eval" . . . for i in 1 2 3 4 5; do eval valore=$i # valore=$i ha lo stesso effetto. "eval", in questo caso, non è necessario. # Una variabile senza meta-significato valuta se stessa -- #+ non può espandersi a nient'altro che al proprio contenuto letterale. echo $valore done echo echo "---" echo for i in ls df; do valore=eval $i # valore=$i in questo caso avrebbe un effetto completamente diverso. # "eval" valuta i comandi "ls" e "df" . . . # I termini "ls" e "df" hanno un meta-significato, #+ dal momento che sono interpretati come comandi #+ e non come stringhe di caratteri. echo $valore done exit 0 |
Esempio 14-12. Forzare un log-off
#!/bin/bash Terminare ppp per forzare uno scollegamento. Lo script deve essere eseguito da root. terminappp="eval kill -9 `ps ax | awk '/ppp/ { print $1 }'`" # ----- ID di processo di ppp ------ $terminappp # La variabile è diventata un comando. # Le operazioni seguenti devono essere eseguite da root. chmod 666 /dev/ttyS3 # Ripristino dei permessi di lettura+scrittura, #+ altrimenti? # Quando si invia un SIGKILL a ppp i permessi della porta seriale vengono #+ modificati, quindi vanno ripristinati allo stato precedente il SIGKILL. rm /var/lock/LCK..ttyS3 # Cancella il lock file della porta seriale. Perché? exit 0 # Esercizi: # -------- # 1) Lo script deve verificare se è stato root ad invocarlo. # 2) Effettuate un controllo per verificare che, prima di tentarne la chiusura, #+ il processo che deve essere terminato sia effettivamente in esecuzione. # 3) Scrivete una versione alternativa dello script basata su 'fuser': #+ if [ fuser -s /dev/modem ]; then . . . |
Esempio 14-13. Una versione di rot13
#!/bin/bash # Una versione di "rot13" usando 'eval'. # Confrontatelo con l'esempio "rot13.sh". impvar_rot_13() # Codifica "rot13" { local nomevar=$1 valoreval=$2 eval $nomevar='$(echo "$valoreval" | tr a-z n-za-m)' } impvar_rot_13 var "foobar" # Codifica "foobar" con rot13. echo $var # sbbone impvar_rot_13 var "$var" # Codifica "sbbone" con rot13. # Ritorno al valore originario della variabile. echo $var # foobar # Esempio di Stephane Chazelas. # Modificato dall'autore del documento. exit 0 |
Rory Winston ha fornito il seguente esempio che dimostra quanto possa essere utile eval.
Esempio 14-14. Utilizzare eval per forzare una sostituzione di variabile in uno script Perl
Nello script Perl "test.pl": ... my $WEBROOT = <WEBROOT_PATH>; ... Per forzare la sostituzione di variabile provate: $export WEBROOT_PATH=/usr/local/webroot $sed 's/<WEBROOT_PATH>/$WEBROOT_PATH/' < test.pl > out Ma questo dà solamente: my $WEBROOT = $WEBROOT_PATH; Tuttavia: $export WEBROOT_PATH=/usr/local/webroot $eval sed 's%\<WEBROOT_PATH\>%$WEBROOT_PATH%' < test.pl > out # ==== Che funziona bene, eseguendo l'attesa sostituzione: my $WEBROOT = /usr/local/webroot; ### Correzioni all'esempio originale eseguite da Paulo Marcel Coelho Aragao. |
Il comando eval può essere rischioso e normalmente, quando esistono alternative ragionevoli, dovrebbe essere evitato. Un eval $COMANDI esegue tutto il contenuto di COMANDI, che potrebbe riservare spiacevoli sorprese come un rm -rf *. Eseguire del codice non molto familiare contenente un eval, e magari scritto da persone sconosciute, significa vivere pericolosamente. |
Il comando set modifica il valore delle variabili interne di uno script. Un possibile uso è quello di attivare/disattivare le modalità (opzioni), legate al funzionamento della shell, che determinano il comportamento dello script. Un'altra applicazione è quella di reimpostare i parametri posizionali passati ad uno script con il risultato dell'istruzione (set `comando`). Lo script assume i campi dell'output di comando come parametri posizionali.
Esempio 14-15. Utilizzare set con i parametri posizionali
#!/bin/bash # script "set-test" # Invocate lo script con tre argomenti da riga di comando, # per esempio, "./set-test uno due tre". echo echo "Parametri posizionali prima di set \`uname -a\` :" echo "Argomento nr.1 da riga di comando = $1" echo "Argomento nr.2 da riga di comando = $2" echo "Argomento nr.3 da riga di comando = $3" set `uname -a` # Imposta i parametri posizionali all'output # del comando `uname -a` echo $_ # Sconosciuto # Opzioni impostate nello script. echo "Parametri posizionali dopo set \`uname -a\` :" # $1, $2, $3, ecc. reinizializzati col risultato di `uname -a` echo "Campo nr.1 di 'uname -a' = $1" echo "Campo nr.2 di 'uname -a' = $2" echo "Campo nr.3 di 'uname -a' = $3" echo --- echo $_ # --- echo exit 0 |
Divertirsi ancora con i parametri posizionali.
Esempio 14-16. Invertire i parametri posizionali
#!/bin/bash # revposparams.sh: inverte i parametri posizionali. # Script di Dan Jacobson, con revisioni stilistiche dell'autore del documento. set a\ b c d\ e; # ^ ^ Spazi con escaping # ^ ^ Spazi senza escaping OIFS=$IFS; IFS=:; # ^ Salva il precedente IFS e imposta quello nuovo. echo until [ $# -eq 0 ] do # Scorre i parametri posizionali. echo "### k0 = "$k"" # Prima k=$1:$k; # Accoda ciascun parametro nella variabile del ciclo. # ^ echo "### k = "$k"" # Dopo echo shift; done set $k # Imposta i nuovi parametri posizionali. echo - echo $# # Conteggio dei parametri posizionali. echo - echo for i # Omettendo "in lista" si imposta la variabile -- i -- #+ ai parametri posizionali. do echo $i # Visualizza i nuovi parametri posizionali. done IFS=$OIFS # Ripristina IFS. # Domande: # È necessario impostare un nuovo IFS, internal field separator, #+ perché lo script funzioni correttamente? # Cosa succede se non viene fatto? Provate. # E perché usare un nuovo IFS -- i due punti -- alla riga 17, #+ per l'accodamento nella variabile del ciclo? # Qual'è il suo scopo? exit 0 $ ./revposparams.sh ### k0 = ### k = a b ### k0 = a b ### k = c a b ### k0 = c a b ### k = d e c a b - 3 - d e c a b |
Invocando set senza alcuna opzione, o argomento, viene visualizzato semplicemente l'elenco di tutte le variabili d'ambiente, e non solo, che sono state inizializzate.
bash$ set AUTHORCOPY=/home/bozo/posts BASH=/bin/bash BASH_VERSION=$'2.05.8(1)-release' ... XAUTHORITY=/home/bozo/.Xauthority _=/etc/bashrc variabile22=abc variabile23=xzy |
set con l'opzione
--
assegna in modo esplicito il
contenuto della variabile ai parametri posizionali. Se non
viene specificata nessuna variabile dopo
--
, i parametri posizionali vengono
annullati.
Esempio 14-17. Riassegnare i parametri posizionali
#!/bin/bash variabile="uno due tre quattro cinque" set -- $variabile # Imposta i parametri posizionali al contenuto di "$variabile". primo_param=$1 secondo_param=$2 shift; shift # Salta i primi due parametri posizionali. # Funziona anche shift 2 restanti_param="$*" echo echo "primo parametro = $primo_param" # uno echo "secondo parametro = $secondo_param" # due echo "rimanenti parametri = $restanti_param" # tre quattro cinque echo; echo # Ancora. set -- $variabile primo_param=$1 secondo_param=$2 echo "primo parametro = $primo_param" # uno echo "secondo parametro = $secondo_param" # due # ====================================================== set -- # Annulla i parametri posizionali quando non viene specificata #+ nessuna variabile. primo_param=$1 secondo_param=$2 echo "primo parametro = $primo_param" # (valore nullo) echo "secondo parametro = $secondo_param" # (valore nullo) exit 0 |
Vedi anche Esempio 10-2 e Esempio 15-51.
il comando unset annulla una variabile di shell, vale a dire, la imposta al valore nullo. Fate attenzione che questo comando non è applicabile ai parametri posizionali.
bash$ unset PATH bash$ echo $PATH bash$ |
Esempio 14-18. "Annullare" una variabile
#!/bin/bash # unset.sh: Annullare una variabile. variabile=ciao # Inizializzata. echo "variabile = $variabile" unset variabile # Annullata. # Stesso effetto di: variabile= echo "variabile (annullata) = $variabile" # $variabile è nulla. if [ -z "$variabile" ] # Prova una verifica di lunghezza #+ della stringa. then echo "\$variabile ha lunghezza zero." fi exit 0 |
Il comando export rende disponibili le variabili a tutti i processi figli generati dallo script in esecuzione o dalla shell. Purtroppo, non vi è alcun modo per esportare le variabili in senso contrario verso il processo genitore, ovvero nei confronti del processo che ha chiamato o invocato lo script o la shell. Un uso importante del comando export si trova nei file di avvio (startup) per inizializzare e rendere accessibili le variabili d'ambiente ai susseguenti processi utente.
Esempio 14-19. Utilizzare export per passare una variabile ad uno script awk incorporato
#!/bin/bash # Ancora un'altra versione dello script "column totaler" #+ (col-totaler.sh) che aggiunge una specifica colonna (di numeri) #+ nel file di destinazione. Qui viene usato l'ambiente per passare #+ una variabile dello script ad 'awk'... e inserire lo script awk #+ in una variabile. ARG=2 E_ERR_ARG=65 if [ $# -ne "$ARG" ] # Verifica il corretto numero di argomenti da #+ riga di comando. then echo "Utilizzo: `basename $0` nomefile colonna-numero" exit $E_ERR_ARG fi nomefile=$1 colonna_numero=$2 #===== Fino a questo punto è uguale allo script originale =====# export colonna_numero # Esporta il numero di colonna all'ambiente, in modo che sia disponibile #+ all'utilizzo. # ------------------------------------------------ scriptawk='{ totale += $ENVIRON["colonna_numero"]} END { print totale }' # Sì, una variabile può contenere uno script awk. # ------------------------------------------------ # Ora viene eseguito lo script awk. awk $scriptawk $nomefile # Grazie, Stephane Chazelas. exit 0 |
È possibile inizializzare ed esportare variabili con un'unica operazione, come export var1=xxx. Tuttavia, come ha sottolineato Greg Keraunen, in certe situazioni questo può avere un effetto diverso da quello che si avrebbe impostando prima la variabile ed esportandola successivamente.
|
I comandi declare e typeset specificano e/o limitano le proprietà delle variabili.
Come declare -r, imposta una variabile in sola lettura ovvero, in realtà, come una costante. I tentativi per modificare la variabile falliscono generando un messaggio d'errore. È l'analogo shell del qualificatore di tipo const del linguaggio C.
Questo potente strumento verifica gli argomenti passati da riga di comando allo script. È l'analogo Bash del comando esterno getopt e della funzione di libreria getopt familiare ai programmatori in C. Permette di passare e concatenare più opzioni [2] e argomenti associati allo script (per esempio nomescript -abc -e /usr/local).
Il costrutto getopts utilizza due
variabili implicite. $OPTIND
, che è
il puntatore all'argomento,
(OPTion INDex) e
$OPTARG
(OPTion ARGument) l'argomento
(eventuale) associato ad un'opzione. Nella dichiarazione, i
due punti che seguono il nome dell'opzione servono ad
indicare che quell'opzione ha associato un argomento.
Il costrutto getopts di solito si
trova all'interno di un ciclo while
che elabora le opzioni e gli argomenti uno alla volta e quindi
incrementa la variabile implicita $OPTIND
per il
passo successivo.
|
while getopts ":abcde:fg" Opzione # Dichiarazione iniziale. # a, b, c, d, e, f, g sono le opzioni attese. # I : dopo l'opzione 'e' indicano che c'è un argomento associato. do case $Opzione in a ) # Fa qualcosa con la variabile 'a'. b ) # Fa qualcosa con la variabile 'b'. ... e) # Fa qualcosa con 'e', e anche con $OPTARG, # che è l'argomento associato all'opzione 'e'. ... g ) # Fa qualcosa con la variabile 'g'. esac done shift $(($OPTIND - 1)) # Sposta il puntatore all'argomento successivo. # Tutto questo non è affatto complicato come sembra <sorriso>. |
Esempio 14-20. Utilizzare getopts per leggere le opzioni/argomenti passati ad uno script
#!/bin/bash # Prove con getopts e OPTIND # Script modificato il 9/10/03 su suggerimento di Bill Gradwohl. # Osserviamo come 'getopts' elabora gli argomenti passati allo script da #+ riga di comando. # Gli argomenti vengono verificati come "opzioni" (flag) #+ ed argomenti associati. # Provate ad invocare lo script con # 'nomescript -mn' # 'nomescript -oq qOpzione' (qOpzione può essere una stringa qualsiasi.) # 'nomescript -qXXX -r' # # 'nomescript -qr' - Risultato inaspettato, considera "r" #+ come l'argomento dell'opzione "q" # 'nomescript -q -r' - Risultato inaspettato, come prima. # 'nomescript -mnop -mnop' - Risultato inaspettato # (OPTIND non è attendibile nello stabilire da dove proviene un'opzione). # # Se un'opzione si aspetta un argomento ("flag:"), viene presa # qualunque cosa si trovi vicino. NO_ARG=0 E_ERR_OPZ=65 if [ $# -eq "$NO_ARG" ] # Lo script è stato invocato senza #+ alcun argomento? then echo "Utilizzo: `basename $0` opzioni (-mnopqrs)" exit $E_ERR_OPZ # Se non ci sono argomenti, esce e #+ spiega come usare lo script. fi # Utilizzo: nomescript -opzioni # Nota: è necessario il trattino (-) while getopts ":mnopq:rs" Opzione do case $Opzione in m ) echo "Scenario nr.1: opzione -m- [OPTIND=${OPTIND}]";; n | o ) echo "Scenario nr.2: opzione -$Opzione- [OPTIND=${OPTIND}]";; p ) echo "Scenario nr.3: opzione -p- [OPTIND=${OPTIND}]";; q ) echo "Scenario nr.4: opzione -q-\ con argomento \"$OPTARG\" [OPTIND=${OPTIND}]";; # Notate che l'opzione 'q' deve avere un argomento associato, # altrimenti salta alla voce predefinita del costrutto case. r | s ) echo "Scenario nr.5: opzione -$Opzione-"'';; * ) echo "È stata scelta un'opzione non implementata.";; # DEFAULT esac done shift $(($OPTIND - 1)) # Decrementa il puntatore agli argomenti in modo che punti al successivo. # $1 fa ora riferimento al primo elemento non-opzione fornito da riga di #+ comando, ammesso che ci sia. exit 0 # Come asserisce Bill Gradwohl, # "Il funzionamento di getopts permette di specificare: nomescript -mnop -mnop, #+ ma non esiste, utilizzando OPTIND, nessun modo affidabile per differenziare #+ da dove proviene che cosa." |
Questa istruzione, se invocata da riga di comando, esegue uno script. All'interno di uno script, source nome-file carica il file nome-file. Caricando un file (comando-punto) si importa codice all'interno dello script, accodandolo (stesso effetto della direttiva #include di un programma C ). Il risultato finale è uguale all'"inserimento" di righe di codice nel corpo dello script. È utile in situazioni in cui diversi script usano un file dati comune o una stessa libreria di funzioni.
Esempio 14-21. "Includere" un file dati
#!/bin/bash . file-dati # Carica un file dati. # Stesso effetto di "source file-dati", ma più portabile. # Il file "file-dati" deve essere presente nella directory di lavoro #+ corrente, poiché vi si fa riferimento per mezzo del suo 'basename'. # Ora utilizziamo alcuni dati del file. echo "variabile1 (dal file-dati) = $variabile1" echo "variabile3 (dal file-dati) = $variabile3" let "sommma = $variabile2 + $variabile4" echo "Somma della variabile2 + variabile4 (dal file-dati) = $somma" echo "messaggio1 (dal file-dati) \"$messaggio1\"" # Nota: apici doppi con escape. visualizza_messaggio Questa è la funzione di visualizzazione messaggio \ presente in file-dati. exit 0 |
Il file file-dati per l'Esempio 14-21 precedente. Dev'essere presente nella stessa directory.
# Questo è il file dati caricato dallo script. # File di questo tipo possono contenere variabili, funzioni, ecc. # Può essere caricato con il comando 'source' o '.' da uno script di shell. # Inizializziamo alcune variabili. variabile1=22 variabile2=474 variabile3=5 variabile4=97 messaggio1="Ciao, come stai?" messaggio2="Per ora piuttosto bene. Arrivederci." visualizza_messaggio () { # Visualizza qualsiasi messaggio passato come argomento. if [ -z "$1" ] then return 1 # Errore, se l'argomento è assente. fi echo until [ -z "$1" ] do # Scorre gli argomenti passati alla funzione. echo -n "$1" # Visualizza gli argomenti uno alla volta, eliminando i ritorni a capo. echo -n " " # Inserisce degli spazi tra le parole. shift # Successivo. done echo return 0 } |
Se il file caricato con source è anch'esso uno script eseguibile, verrà messo in esecuzione e, alla fine, il controllo ritornerà allo script che l'ha richiamato. A questo scopo, uno script eseguibile caricato con source può usare return.
Si possono passare (opzionalmente) degli argomenti al file caricato con source come parametri posizionali.
source $nomefile $arg1 arg2 |
È anche possibile per uno script usare source in riferimento a se stesso, sebbene questo non sembri avere delle applicazioni pratiche.
Esempio 14-22. Un (inutile) script che "carica" se stesso
#!/bin/bash # self-source.sh: uno script che segue se stesso "ricorsivamente." # Da "Stupid Script Tricks," Volume II. MAXPASSCNT=100 # Numero massimo di esecuzioni. echo -n "$conta_passi " # Al primo passaggio, vengono visualizzati solo due spazi, #+ perché $conta_passi non è stata inizializzata. let "conta_passi += 1" # Si assume che la variabile $conta_passi non inizializzata possa essere #+ incrementata subito. # Questo funziona con Bash e pdksh, ma si basa su un'azione non portabile #+ (e perfino pericolosa). # Sarebbe meglio impostare $conta_passi a 0 prima che venga incrementata. while [ "$conta_passi" -le $MAXPASSCNT ] do . $0 # Lo script "esegue" se stesso, non chiama se stesso. # ./$0 (che sarebbe la vera ricorsività) in questo caso non funziona. # Perché? done # Quello che avviene in questo script non è una vera ricorsività, perché lo #+ script in realtà "espande" se stesso, vale a dire genera una nuova #+ sezione di codice ad ogni passaggio attraverso il ciclo 'while', #+ con ogni 'source' che si trova alla riga 20. # # Naturalmente, lo script interpreta ogni succesiva 'esecuzione' della riga #+ con "#!" come un commento e non come l'inizio di un nuovo script. echo exit 0 # Il risultato finale è un conteggio da 1 a 100. # Molto impressionante. # Esercizio: # --------- # Scrivete uno script che usi questo espediente per fare qualcosa #+ di veramente utile. |
Termina in maniera incondizionata uno script. [3] Il comando exit opzionalmente può avere come argomento un intero che viene restituito alla shell come exit status dello script. È buona pratica terminare tutti gli script, tranne quelli più semplici, con exit 0, indicandone con ciò la corretta esecuzione.
Se uno script termina con un exit senza argomento, l'exit status dello script corrisponde a quello dell'ultimo comando eseguito nello script, escludendo exit. Equivale a exit $?. |
Il comando exit può anche essere usato per terminare una subshell. |
Questo builtin di shell sostituisce il processo corrente con un comando specificato. Normalmente, quando la shell incontra un comando, genera (forking) un processo figlio che è quello che esegue effettivamente il comando. Utilizzando il builtin exec, la shell non esegue il forking ed il comando lanciato con exec sostituisce la shell. Se viene usato in uno script, quindi, ne forza l'uscita quando il comando eseguito con exec termina. [4]
Esempio 14-23. Effetti di exec
#!/bin/bash exec echo "Uscita da \"$0\"." # Esce dallo script in questo punto. # -------------------------------------------- # Le righe seguenti non verranno mai eseguite. echo "Questo messaggio non verrà mai visualizzato." exit 99 # Questo script non termina qui. # Verificate l'exit status, dopo che lo script è #+ terminato, con 'echo $?'. # *Non* sarà 99. |
Esempio 14-24. Uno script che esegue se stesso con exec
#!/bin/bash # self-exec.sh echo echo "Sebbene questa riga compaia UNA SOLA VOLTA nello script, continuerà" echo "ad essere visualizzata." echo "Il PID di questo script d'esempio è ancora $$." # Dimostra che non viene generata una subshell. echo "==================== Premi Ctl-C per uscire ====================" sleep 1 exec $0 # Inizia un'altra istanza di questo stesso script #+ che sostituisce quella precedente. echo "Questa riga non verrà mai visualizzata" # Perché? exit 0 |
exec serve anche per riassegnare i descrittori dei file. Per esempio, exec <zzz-file sostituisce lo stdin con il file zzz-file.
L'opzione |
Questo comando permette di cambiare le opzioni di shell al volo (vedi Esempio 24-1 e Esempio 24-2). Appare spesso nei file di avvio (startup) Bash, ma può essere usato anche in altri script. È necessaria la versione 2 o seguenti di Bash.
shopt -s cdspell # Consente le errate digitazioni, non gravi, dei nomi delle directory quando #+ si usa 'cd' cd /hpme # Oops! Errore '/home'. pwd # /home # La shell ha corretto l'errore di digitazione. |
Inserendo il comando caller all'interno di una funzione vengono visualizzate allo stdout informazioni su chi ha richiamato quella funzione.
#!/bin/bash funzione1 () { # All'interno di funzione1 (). caller 0 # Dimmi tutto. } funzione1 # Riga 9 dello script. # 9 main test.sh # ^ Numero della riga dove la funzione è stata richiamata. # ^^^^ Invocata dalla parte "main" dello script. # ^^^^^^^ Nome dello script chiamante. caller 0 # Non ha alcun effetto perché non è in una funzione. |
Il comando caller può restituire anche informazioni sul chiamante se inserita uno script caricato con source all'interno di un altro script. Come una funzione, si tratta di una "chiamata di subroutine."
Questo comando potrebbe essere utile nel debugging.
Comando che restituisce zero come exit status di una corretta esecuzione, ma nient'altro.
bash$ true bash$ echo $? 0 |
# Ciclo infinito while true # alternativa a ":" do operazione-1 operazione-2 ... operazione-n # Occorre un sistema per uscire dal ciclo, altrimenti lo script si blocca. done |
Comando che restituisce l'exit status di una esecuzione non andata a buon fine, ma nient'altro.
bash$ false bash$ echo $? 1 |
Prova di "false" if false then echo "false valuta \"vero\"" else echo "false valuta \"falso\"" fi # false valuta "falso" # Ciclo while "falso" (ciclo nullo) while false do # Il codice seguente non verrà eseguito. operazione-1 operazione-2 ... operazione-n # Non succede niente! done |
Simile al comando esterno
which,
type comando identifica
"comando". A differenza di which,
type è un builtin di Bash. L'utile opzione
-a
di type identifica le
parole chiave ed i
builtin, individuando anche i
comandi di sistema che hanno gli stessi nomi.
bash$ type '[' [ is a shell builtin bash$ type -a '[' [ is a shell builtin [ is /usr/bin/[ bash$ type type type is a shell builtin |
Registra i percorsi assoluti dei comandi
specificati -- nella tabella degli hash della shell
[5]
-- in modo che la shell o lo script non avranno bisogno di cercare
$PATH
nelle successive chiamate di quei
comandi. Se hash viene eseguito senza
argomenti, elenca semplicemente i comandi presenti nella
tabella. L'opzione -r
cancella la tabella
degli hash.
Il builtin bind visualizza o modifica a configurazione d'uso della tastiera tramite readline [6].
Fornisce un breve riepilogo dell'utilizzo di un builtin di shell. È il corrispettivo di whatis, per i builtin.
bash$ help exit exit: exit [n] Exit the shell with a status of N. If N is omitted, the exit status is that of the last command executed. |
Alcuni dei seguenti comandi di controllo di job possono avere come argomento un "identificatore di job". Vedi la tabella alla fine del capitolo.
Elenca i job in esecuzione in background, fornendo il rispettivo numero. Non è così utile come ps.
È facilissimo confondere job e processi. Alcuni builtin, quali kill, disown e wait, accettano come argomento sia il numero di job che quello di processo. I comandi fg, bg e jobs accettano solo il numero di job.
"1" è il numero di job (i job sono gestiti dalla shell corrente), mentre "1384" è il numero di processo (i processi sono gestiti dal sistema operativo). Per terminare questo job/processo si può utilizzare sia kill %1 che kill 1384. Grazie, S.C. |
Cancella il/i job dalla tabella dei job attivi della shell.
Il comando fg modifica l'esecuzione di un job da background (sfondo) in foreground (primo piano). Il comando bg fa ripartire un job che era stato sospeso, mettendolo in esecuzione in background. Se non viene specificato nessun numero di job, allora il comando fg o bg agisce sul job attualmente in esecuzione.
Arresta l'esecuzione dello script finché tutti i job in esecuzione in background non sono terminati, o finché non è terminato il job o il processo il cui ID è stato passato come opzione. Restituisce l'exit status di attesa-comando.
Il comando wait può essere usato per evitare che uno script termini prima che un job in esecuzione in background abbia ultimato il suo compito (ciò creerebbe un temibile processo orfano).
Esempio 14-25. Attendere la fine di un processo prima di continuare
#!/bin/bash ROOT_UID=0 # Solo gli utenti con $UID 0 posseggono i privilegi di root. E_NONROOT=65 E_NOPARAM=66 if [ "$UID" -ne "$ROOT_UID" ] then echo "Bisogna essere root per eseguire questo script." # "Cammina ragazzo, hai finito di poltrire." exit $E_NONROOT fi if [ -z "$1" ] then echo "Utilizzo: `basename $0` nome-cercato" exit $E_NOPARAM fi echo "Aggiornamento del database 'locate' ..." echo "Questo richiede un po' di tempo." updatedb /usr & # Deve essere eseguito come root. wait # Non viene eseguita la parte restante dello script finché 'updatedb' non #+ ha terminato il proprio compito. # Si vuole che il database sia aggiornato prima di cercare un nome di file. locate $1 # Senza il comando wait, nell'ipotesi peggiore, lo script sarebbe uscito #+ mentre 'updatedb' era ancora in esecuzione, trasformandolo in un processo #+ orfano. exit 0 |
Opzionalmente, wait può avere come argomento un identificatore di job, per esempio, wait%1 o wait $PPID. Vedi la tabella degli identificatori di job.
In uno script, far eseguire un comando in background, per mezzo della E commerciale (&), può causare la sospensione dello script finché non viene premuto il tasto INVIO. Questo sembra capitare con i comandi che scrivono allo stdout. Può rappresentare un grande fastidio.
Mettendo wait dopo il comando che deve essere eseguito in background si rimedia a questo comportamento.
|
Ha un effetto simile a Control-Z, ma sospende la shell (il processo genitore della shell può, ad un certo momento stabilito, farle riprendere l'esecuzione).
È il comando di uscita da una shell di login. Facoltativamente può essere specificato un exit status.
Fornisce statistiche sul tempo di sistema impiegato per l'esecuzione dei comandi, nella forma seguente:
0m0.020s 0m0.020s |
Questo comando ha un valore molto limitato perché non è di uso comune tracciare profili o benchmark degli script di shell.
Termina immediatamente un processo inviandogli un appropriato segnale di terminazione (vedi Esempio 16-6).
Esempio 14-26. Uno script che uccide sé stesso
#!/bin/bash # self-destruct.sh kill $$ # Lo script in questo punto "uccide" il suo stesso processo. # Ricordo che "$$" è il PID dello script. echo "Questa riga non viene visualizzata." # Invece, la shell invia il messaggio "Terminated" allo stdout. exit 0 # Dopo che lo script è terminato prematuramente, qual'è l'exit #+ status restituito? # # sh self-destruct.sh # echo $? # 143 # # 143 = 128 + 15 # segnale SIGTERM |
kill -l elenca tutti i segnali (come fa il file /usr/include/asm/signal.h). kill -9 è il "killer infallibile", che solitamente interrompe un processo che si rifiuta ostinatamente di terminare con un semplice kill. Talvolta funziona anche kill -15. Un "processo zombie", vale a dire un processo figlio che è stato terminato, ma il cui processo genitore non lo è stato (ancora), non può essere ucciso -- non si può uccidere qualcosa che è già morto. Comunque init, presto o tardi, solitamente lo cancellerà. |
Il comando killall termina un processo in esecuzione usando il nome invece che l'ID di processo. Se di un particolare comando sono in esecuzione istanze multiple, allora un killall su quel comando le terminarà tutte.
Qui si fa riferimento al comando killall in /usr/bin, non allo script killall presente in /etc/rc.d/init.d. |
La direttiva command disabilita gli alias e le funzioni del comando che lo segue.
bash$ command ls |
Invocando builtin COMANDO_BUILTIN viene eseguito "COMANDO_BUILTIN" come se fosse un builtin di shell, disabilitando temporaneamente sia le funzioni che i comandi di sistema esterni aventi lo stesso nome.
Abilita o disabilita un builtin di shell. Ad esempio, enable -n kill disabilita il builtin di shell kill, così quando Bash successivamente incontra un kill, invocherà /bin/kill.
L'opzione
-a
di enable elenca
tutti i builtin di shell, indicando se sono abilitati o
meno. L'opzione -f nomefile
permette ad enable di caricare un
builtin come un modulo
di una libreria condivisa (DLL) da un file oggetto
correttamente compilato.
[7].
È un adattamento per Bash dell'autoloader ksh. In presenza di un autoload , viene caricata una funzione contenente una dichiarazione "autoload" da un file esterno, alla sua prima invocazione. [8] Questo fa risparmiare risorse di sistema.
È da notare che autoload non fa parte dell'installazione normale di Bash. Bisogna caricarlo con enable -f (vedi sopra).
Tabella 14-1. Identificatori di job
Notazione | Significato |
---|---|
%N | numero associato al job [N] |
%S | Chiamata (da riga di comando) del job che inizia con la stringa S |
%?S | Chiamata (da riga di comando) del job con al suo interno la stringa S |
%% | job "corrente" (ultimo job arrestato in foreground o iniziato in background) |
%+ | job "corrente" (ultimo job arrestato in foreground o iniziato in background) |
%- | Ultimo job |
$! | Ultimo processo in background |
[1] | Un'eccezione è rappresentata dal comando time, citato nella documentazione ufficiale Bash come parola chiave. |
[2] | Un'opzione è un argomento che funziona come un interruttore, attivando/disattivando le modalità di azione di uno script. L'argomento associato ad una particolare opzione ne indica o meno l'abilitazione. |
[3] | Tecnicamente, exit termina solamente il processo (o la shell) in cui è in esecuzione, non il processo genitore. |
[4] | Tranne quando exec viene usato per riassegnare i descrittori dei file. |
[5] | L'hashing è un metodo per la creazione di chiavi di ricerca per i dati registrati in una tabella. Le chiavi vengono create "codificando" i dati stessi per mezzo di uno tra i numerosi e semplici algoritmi matematici (metodi, o formule). Il vantaggio dell'hashing è la velocità. Lo svantaggio è che possono verificarsi delle "collisioni" -- quando una sola chiave fa riferimento a più di un dato. Per esempi di hashing vedi Esempio A-21 e Esempio A-22. |
[6] | La libreria readline è quella che Bash utilizza per leggere l'input in una shell interattiva. |
[7] | I sorgenti C per un certo numero di builtin caricabili, solitamente, si trovano nella directory/usr/share/doc/bash-?.??/functions. E' da notare che l'opzione |
[8] | Lo stesso risultato di autoload può essere ottenuto con typeset -fu. |