Capitolo 9. Variabili riviste

Sommario
9.1. Variabili interne
9.2. Manipolazione di stringhe
9.3. Sostituzione di parametro
9.4. Tipizzare le variabili: declare o typeset
9.5. Referenziazione indiretta delle variabili
9.6. $RANDOM: genera un intero casuale
9.7. Il costrutto doppie parentesi

Utilizzate in modo appropriato, le variabili possono aumentare la potenza e la flessibilità degli script. Per questo è necessario conoscere tutte le loro sfumature e sottigliezze.

9.1. Variabili interne

Variabili builtin (incorporate)

sono quelle variabili che determinano il comportamento dello script bash

$BASH

il percorso dell'eseguibile Bash

bash$ echo $BASH 
/bin/bash

$BASH_ENV

variabile d'ambiente che punta al file di avvio di Bash, che deve essere letto quando si invoca uno script

$BASH_SUBSHELL

variabile che indica il livello della subshell. Si tratta di una nuova variabile aggiunta in Bash, versione 3.

Per il suo impiego vedi Esempio 20-1.

$BASH_VERSINFO[n]

un array di 6 elementi contenente informazioni sulla versione Bash installata. È simile a $BASH_VERSION, vedi oltre, ma più dettagliata.

# Informazioni sulla versione Bash:

for n in 0 1 2 3 4 5
do
  echo "BASH_VERSINFO[$n] = ${BASH_VERSINFO[$n]}"
done  

# BASH_VERSINFO[0] = 3                      # Nr. della major version.
# BASH_VERSINFO[1] = 00                     # Nr. della minor version.
# BASH_VERSINFO[2] = 14                     # Nr. del patch level.
# BASH_VERSINFO[3] = 1                      # Nr. della build version.
# BASH_VERSINFO[4] = release                # Stato della release.
# BASH_VERSINFO[5] = i386-redhat-linux-gnu  # Architettura.
                                            # (uguale a $MACHTYPE).

$BASH_VERSION

la versione Bash installata

bash$ echo $BASH_VERSION
3.00.14(1)-release
	      

tcsh% echo $BASH_VERSION
BASH_VERSION: Undefined variable.
	      

Un buon metodo per determinare quale shell è in funzione è quello di verificare $BASH_VERSION. $SHELL potrebbe non fornire necessariamente una risposta corretta.

$DIRSTACK

il contenuto della locazione più alta dello stack delle directory (determinato da pushd e popd)

Questa variabile corrisponde al comando dirs, senonché dirs mostra l'intero contenuto dello stack delle directory.

$EDITOR

l'editor di testo predefinito invocato da uno script, solitamente vi o emacs.

$EUID

numero ID "effettivo" dell'utente

Numero identificativo dell'utente corrente corrispondente a qualsiasi identità egli abbia assunto, solitamente tramite il comando su.

Attenzione

$EUID, di conseguenza, non è necessariamente uguale a $UID.

$FUNCNAME

nome della funzione corrente

xyz23 ()
{
  echo "$FUNCNAME è in esecuzione."  # xyz23 è in esecuzione. 
}

xyz23

echo "NOME FUNZIONE = $FUNCNAME"     # NOME FUNZIONE =
                                     # Valore nullo all'esterno della funzione.

$GLOBIGNORE

un elenco di nomi di file da escludere dalla ricerca nel globbing

$GROUPS

i gruppi a cui appartiene l'utente corrente

È l'elenco (array) dei numeri id dei gruppi a cui appartiene l'utente corrente, così come sono registrati nel file /etc/passwd.

root# echo $GROUPS
0


root# echo ${GROUPS[1]}
1


root# echo ${GROUPS[5]}
6
	      

$HOME

directory home dell'utente, di solito /home/nomeutente (vedi Esempio 9-15)

$HOSTNAME

In fase di boot, il comando hostname, presente in uno script init, assegna il nome del sistema. Tuttavia è la funzione gethostname() che imposta la variabile interna Bash $HOSTNAME. Vedi anche Esempio 9-15.

$HOSTTYPE

tipo di macchina

Come $MACHTYPE, identifica il sistema hardware, ma in forma ridotta.

bash$ echo $HOSTTYPE
i686
$IFS

separatore di campo (internal field separator)

Questa variabile determina il modo in cui Bash riconosce i campi, ovvero le singole parole, nell'interpretazione delle stringhe di caratteri.

Il valore preimpostato è una spaziatura (spazio, tabulazione e ritorno a capo), ma può essere modificato, per esempio, per verificare un file dati che usa la virgola come separatore di campo. E' da notare che $* utilizza il primo carattere contenuto in $IFS. Vedi Esempio 5-1.

bash$ echo $IFS | cat -vte
$
(Mostra le tabulazioni e visualizza "$" a fine-riga.)



bash$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z
(Legge i comandi da una stringa e assegna gli argomenti ai parametri posizionali.)
	      

Attenzione

$IFS non tratta la spaziatura allo stesso modo degli altri caratteri.

Esempio 9-1. $IFS e gli spazi

#!/bin/bash
# $IFS gestisce gli spazi in modo diverso dagli altri caratteri.

output_arg_uno_per_riga()
{
  for arg
  do echo "[$arg]"
  done
}

echo; echo "IFS=\" \""
echo "-------"

IFS=" "
var=" a  b c   "
output_arg_uno_per_riga $var   # output_arg_uno_per_riga `echo " a  b c   "`
#
# [a]
# [b]
# [c]


echo; echo "IFS=:"
echo "-----"

IFS=:
var=":a::b:c:::"               # Come prima, ma con ":" anziché " ".
output_arg_uno_per_riga $var
#
# []
# [a]
# []
# [b]
# [c]
# []
# []
# []

# In awk si ottiene lo stesso risultato con il separatore di campo "FS".

# Grazie, Stephane Chazelas.

echo

exit 0

(Grazie, S. C., per i chiarimenti e gli esempi.)

Vedi anche Esempio 15-37, Esempio 10-7 e Esempio 18-14 per un'istruttiva dimostrazione sull'impiego di $IFS.

$IGNOREEOF

ignora EOF: quanti end-of-file (control-D) la shell deve ignorare prima del logout

$LC_COLLATE

Spesso impostata nei file .bashrc o /etc/profile, questa variabile controlla l'ordine di collazione nell'espansione del nome del file e nella ricerca di corrispondenza. Se mal gestita, LC_COLLATE può provocare risultati inattesi nel globbing dei nomi dei file.

Nota

Dalla versione 2.05 di Bash, il globbing dei nomi dei file non fa più distinzione tra lettere minuscole e maiuscole, in un intervallo di caratteri specificato tra parentesi quadre. Per esempio, ls [A-M]* restituisce sia File1.txt che file1.txt. Per riportare il globbing all'abituale comportamento, si imposti LC_COLLATE a C con export LC_COLLATE=C nel file /etc/profile e/o ~/.bashrc.

$LC_CTYPE

Questa variabile interna controlla l'interpretazione dei caratteri nel globbing e nella ricerca di corrispondenza.

$LINENO

Variabile contenente il numero della riga dello script di shell in cui essa appare. Ha valore solo nello script in cui si trova. È utile in modo particolare nel debugging.

# *** INIZIO BLOCCO DI DEBUGGING ***
ultimo_arg_cmd=$_  # Viene salvato.

echo "Alla riga numero $LINENO, variabile \"v1\" = $v1"
echo "Ultimo argomento eseguito = $ultimo_arg_cmd"
# *** FINE BLOCCO DI DEBUGGING ***

$MACHTYPE

tipo di macchina

Identifica il sistema hardware in modo dettagliato.

bash$ echo $MACHTYPE
i486-slackware-linux-gnu
$OLDPWD

directory di lavoro precedente ("OLD-print-working-directory", la directory in cui vi trovavate prima dell'ultimo comando cd)

$OSTYPE

nome del sistema operativo

bash$ echo $OSTYPE
linux
$PATH

i percorsi delle directory in cui si trovano i file eseguibili (binari), di solito /usr/bin/, /usr/X11R6/bin/, /usr/local/bin, ecc.

Quando viene dato un comando, la shell ricerca automaticamente il percorso dell'eseguibile. Questo è possibile perché tale percorso è memorizzato nella variabile d'ambiente $PATH, che è un elenco di percorsi possibili separati da : (due punti). Di solito il sistema conserva la configurazione di $PATH nel file /etc/profile e/o ~/.bashrc (vedi Appendice G).

bash$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin

PATH=${PATH}:/opt/bin aggiunge la directory /opt/bin ai percorsi predefiniti. Usato in uno script rappresenta un espediente per aggiungere temporaneamente una directory a $PATH. Quando lo script termina viene ripristinato il valore originale di $PATH (questo perché un processo figlio, qual'è uno script, non può modificare l'ambiente del processo genitore, la shell).

Nota

La "directory di lavoro" corrente, ./, di solito per ragioni di sicurezza, non è compresa in $PATH.

$PIPESTATUS

Array contenente lo/gli exit status dell'ultima pipe eseguita in foreground (primo piano). È piuttosto interessante in quanto non fornisce necessariamente come risultato l'exit status dell'ultimo comando eseguito.

bash$ echo $PIPESTATUS
0

bash$ ls -al | comando_errato
bash: comando_errato: command not found
bash$ echo $PIPESTATUS
141

bash$ ls -al | comando_errato
bash: comando_errato: command not found
bash$ echo $?
127
	      

Gli elementi dell'array $PIPESTATUS sono gli exit status dei corrispondenti comandi eseguiti nella pipe. $PIPESTATUS[0] contiene l'exit status del primo comando della pipe, $PIPESTATUS[1] l'exit status del secondo comando, e così via.

Attenzione

La variabile $PIPESTATUS, in una shell di login, potrebbe contenere un errato valore 0 (nelle versioni Bash precedenti alla 3.0).

tcsh% bash

bash$ who | grep nobody | sort
bash$ echo ${PIPESTATUS[*]}
0
	      

I comandi precedenti, eseguiti in uno script, avrebbero prodotto il risultato atteso 0 1 0.

Grazie a Wayne Pollock per la puntualizzazione e per aver fornito l'esempio precedente.

Nota

La variabile $PIPESTATUS, in alcuni contesti, dà risultati inaspettati.

bash$ echo $BASH_VERSION
3.00.14(1)-release

bash$ $ ls | comando_errato | wc
bash: comando_errato: command not found
 0       0       0

bash$ echo ${PIPESTATUS[@]}
141 127 0
	      

Chet Ramey attribuisce il risultato precedente al comportamento di ls. Se ls scrive in una pipe il cui output non viene letto, allora SIGPIPE lo termina, restituendo exit status 141. Altrimenti l'exit status è l'atteso 0. La stessa cosa vale per tr.

Nota

$PIPESTATUS è una variabile "volatile". Deve essere visualizzata immediatamente dopo la pipe, prima che venga eseguito qualsiasi altro comando.

bash$ $ ls | comando_errato | wc
bash: comando_errato: command not found
 0       0       0

bash$ echo ${PIPESTATUS[@]}
0 127 0

bash$ echo ${PIPESTATUS[@]}
0
	      

Nota

L'opzione pipefail può essere utile nei casi in cui $PIPESTATUS non fornisce le informazioni desiderate.

$PPID

Lo $PPID di un processo non è che l'ID di processo (pid) del processo genitore. [1]

Lo si confronti con il comando pidof.

$PROMPT_COMMAND

Variabile che contiene un comando che deve essere eseguito immediatamente prima della visualizzazione del prompt primario $PS1 .

$PS1

È il prompt principale, quello che compare sulla riga di comando.

$PS2

Prompt secondario. Compare quando è atteso un ulteriore input (il comando non è ancora terminato). Viene visualizzato come ">".

$PS3

Prompt di terzo livello, visualizzato in un ciclo select (vedi Esempio 10-29).

$PS4

Prompt di quarto livello. Viene visualizzato all'inizio di ogni riga di output quando lo script è stato invocato con l'opzione -x. Viene visualizzato come "+".

$PWD

Directory di lavoro (directory corrente)

È analoga al comando builtin pwd.

#!/bin/bash

E_ERRATA_DIRECTORY=73

clear # Pulisce lo schermo.

DirectoryDestinazione=/home/bozo/projects/GreatAmericanNovel

cd $DirectoryDestinazione
echo "Cancellazione dei vecchi file in $DirectoryDestinazione."

if [ "$PWD" != "$DirectoryDestinazione" ]
then    # Evita di cancellare per errore una directory sbagliata.
  echo "Directory errata!"
  echo "Sei in $PWD, non in $DirectoryDestinazione!"
  echo "Salvo!"
  exit $E_ERRATA_DIRECTORY
fi  

rm -rf *
rm .[A-Za-z0-9]*    # Cancella i file i cui nomi iniziano con un punto.
# rm -f .[^.]* ..?*   per cancellare file che iniziano con due o più punti.
# (shopt -s dotglob; rm -f *)   anche in questo modo.
# Grazie, S.C. per la puntualizzazione.

#  I nomi dei file possono essere formati da tutti i caratteri nell'intervallo
#+ 0 - 255, tranne "/". La cancellazione di file che iniziano con caratteri 
#+ inconsueti è lasciata come esercizio. 

# Altre eventuali operazioni. 

echo 
echo "Fatto." 
echo "Cancellati i vecchi file in $DirectoryDestinazione." 
echo 


exit 0 

$REPLY

È la variabile preimpostata quando non ne viene fornita alcuna a read. È utilizzabile anche con i menu select. In questo caso, però, fornisce solo il numero che indica la variabile scelta, non il valore della variabile.

#!/bin/bash
# reply.sh

# REPLY è la variabile preimpostata per il comando 'read'.

echo
echo -n "Qual'è la tua verdura preferita?"
read

echo "La tua verdura preferita è $REPLY."
#  REPLY contiene il valore dell'ultimo  "read" se e solo se
#+ non è stata indicata alcuna variabile.

echo
echo -n "Qual'è il tuo frutto preferito?"
read frutto
echo "Il tuo frutto preferito è $frutto."
echo "ma..."
echo "Il valore di \$REPLY è ancora $REPLY."
#  $REPLY è ancora impostato al valore precedente perché
#+ la variabile $frutto contiene il nuovo valore letto con "read".

echo

exit 0

$SECONDS

Numero di secondi trascorsi dall'inizio dell'esecuzione dello script.

#!/bin/bash

TEMPO_LIMITE=10
INTERVALLO=1

echo
echo "Premi Control-C per terminare prima di $TEMPO_LIMITE secondi."
echo

while [ "$SECONDS" -le "$TEMPO_LIMITE" ]
do
  if [ "$SECONDS" -eq 1 ]
  then
    unita=secondo
  else  
    unita=secondi
  fi

  echo "Questo script è in esecuzione da $SECONDS $unita."
  #  Su una macchina lenta o sovraccarica lo script, talvolta,
  #+ potrebbe saltare un conteggio.
  sleep $INTERVALLO
done

echo -e "\a"  # Beep!

exit 0

$SHELLOPTS

l'elenco delle opzioni di shell abilitate. È una variabile in sola lettura

bash$ echo $SHELLOPTS
braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs
	      

$SHLVL

Livello della shell, profondità di annidamento di Bash. [2] Se, da riga di comando $SHLVL vale 1, in uno script questo valore viene aumentato a 2.

Nota

Questa variabile non viene modificata dalle subshell. Si usi $BASH_SUBSHELL quando si ha la necessità di conoscere l'annidamento di una subshell.

$TMOUT

Se la variabile d'ambiente $TMOUT è impostata ad un valore tempo diverso da zero, il prompt della shell termina dopo $tempo secondi. Questo provoca il logout.

Dalla versione Bash 2.05b è possibile utilizzare $TMOUT negli script in combinazione con read.

# Funziona negli script con Bash versione 2.05b e successive.

TMOUT=3    # Imposta il prompt alla durata di tre secondi.

echo "Qual'è la tua canzone preferita?"
echo "Svelto, hai solo $TMOUT secondi per rispondere!"
read canzone

if [ -z "$canzone" ]
then
  canzone="(nessuna risposta)"
  # Risposta preimpostata.
fi

echo "La tua canzone preferita è $canzone."

Esistono altri metodi, più complessi, per implementare un input temporizzato in uno script. Una possibile alternativa è quella di impostare un ciclo di temporizzazione per segnalare allo script quando il tempo è scaduto. Ma anche così è necessaria una routine per la gestione di un segnale per catturare (trap) (vedi Esempio 29-5) l'interrupt generato dal ciclo di temporizzazione (fiu!).

Esempio 9-2. Input temporizzato

#!/bin/bash
# timed-input.sh

# TMOUT=3      Funziona anche questo, a partire dalle più recenti 
#              versioni di Bash.

TEMPOLIMITE=3  #  In questo caso tre secondi. Può essere impostato 
               #+ ad un valore diverso.

VisualizzaRisposta()
{
  if [ "$risposta" = TIMEOUT ]
  then
    echo $risposta
  else       # Ho voluto tenere separati i due esempi. 
    echo "La tua verdura preferita è $risposta"
    kill $!  #  Uccide la funzione AvvioTimer in esecuzione in 
             #+ background perché non più necessaria. $! è il PID
             #+ dell'ultimo job in esecuzione in background.

  fi

}  



AvvioTimer()
{
  sleep $TEMPOLIMITE && kill -s 14 $$ &
  # Attende 3 secondi, quindi invia il segnale SIGALARM allo script.
}  

Int14Vettore()
{
  risposta="TIMEOUT"
  VisualizzaRisposta
  exit 14
}  

trap Int14Vettore 14   # Interrupt del timer (14) modificato allo scopo.

echo "Qual'è la tua verdura preferita? "
AvvioTimer
read risposta
VisualizzaRisposta

#  Ammettiamolo, questa è un'implementazione tortuosa per temporizzare 
#+ l'input, comunque l'opzione "-t" di "read" semplifica il compito.
#  Vedi "t-out.sh" più sotto.

#  Se desiderate qualcosa di più elegante... prendete in considerazione 
#+ la possibilità di scrivere l'applicazione in C o C++,  
#+ utilizzando le funzioni di libreria appropriate, come 'alarm' e 'setitimer'.

exit 0

Un'alternativa è l'utilizzo di stty.

Esempio 9-3. Input temporizzato, un ulteriore esempio

#!/bin/bash
# timeout.sh

#  Scritto da Stephane Chazelas
#+ e modificato dall'autore del libro.

INTERVALLO=5              # intervallo di timeout

leggi_temporizzazione() {
  timeout=$1
  nomevar=$2
  precedenti_impostazioni_tty=`stty -g`
  stty -icanon min 0 time ${timeout}0
  eval read $nomevar      # o semplicemente  read $nomevar
  stty "$precedenti_impostazioni_tty"
  # Vedi la pagina di manuale di "stty".
}

echo; echo -n "Come ti chiami? Presto! "
leggi_temporizzazione $INTERVALLO nome

#  Questo potrebbe non funzionare su tutti i tipi di terminale.
#  Il timeout massimo, infatti, dipende dallo specifico terminale.
#+ (spesso è di 25.5 secondi).

echo

if [ ! -z "$nome" ]  # se il nome è stato immesso prima del timeout...
then
  echo "Ti chiami $nome."
else
  echo "Tempo scaduto."
fi

echo

# Il comportamento di questo script è un po' diverso da "timed-input.sh".
# Ad ogni pressione di tasto, la temporizzazione ricomincia da capo.

exit 0

Forse, il metodo più semplice è quello di usare read con l'opzione -t.

Esempio 9-4. read temporizzato

#!/bin/bash
# t-out.sh 
# Ispirato da un suggerimento di "syngin seven" (grazie).


TEMPOLIMITE=4        # 4 secondi

read -t $TEMPOLIMITE variabile <&1
#                              ^^^
#  In questo esempio, "<&1" è necessario per Bash 1.x e 2.x,
#  ma inutile per Bash 3.x.

echo

if [ -z "$variabile" ]  # È nulla?
then
  echo "Tempo scaduto, la variabile non è stata impostata."
else  
  echo "variabile = $variabile"
fi  

exit 0
$UID

numero ID dell'utente

è il numero identificativo dell'utente corrente com'è registrato nel file /etc/passwd.

Rappresenta l'id reale dell'utente, anche nel caso abbia assunto temporaneamente un'altra identità per mezzo di su. $UID è una variabile in sola lettura e non può essere modificata né da riga di comando né in uno script. È il sostituto del builtin id.

Esempio 9-5. Sono root?

#!/bin/bash
# am-i-root.sh:   Sono root o no?

ROOT_UID=0   # Root ha $UID 0.

if [ "$UID" -eq "$ROOT_UID" ]  #  Il vero "root" avrà la compiacenza 
                               #+ di aspettare?
then
  echo "Sei root."
else
  echo "Sei un utente normale (ma la mamma ti vuol bene lo stesso)."
fi

exit 0


# ===================================================================== #
# Il codice seguente non viene eseguito perché lo script è già terminato.

# Un metodo alternativo per andare al fondo della questione:

NOME_ROOT=root

nomeutente=`id -nu`              # Oppure...   nomeutente=`whoami`
if [ "$nomeutente" = "$NOME_ROOT" ]
then
  echo "Rooty, toot, toot. Sei root."
else
  echo "Sei solo un semplice utente."
fi

Vedi anche Esempio 2-3.

Nota

Le variabili $ENV, $LOGNAME, $MAIL, $TERM, $USER, e $USERNAME non sono builtin di Bash. Vengono, comunque, impostate spesso come variabili d'ambiente in uno dei file di avvio di Bash. $SHELL, è il nome della shell di login dell'utente, può essere impostata dal file /etc/passwd o da uno script "init". Anche questa non è un builtin di Bash.

tcsh% echo $LOGNAME
bozo
tcsh% echo $SHELL
/bin/tcsh
tcsh% echo $TERM
rxvt

bash$ echo $LOGNAME
bozo
bash$ echo $SHELL
/bin/tcsh
bash$ echo $TERM
rxvt
	      

Parametri Posizionali

$0, $1, $2, ecc.

rappresentano i diversi parametri che vengono passati da riga di comando ad uno script, ad una funzione, o per impostare una variabile (vedi Esempio 4-5 e Esempio 14-15)

$#

numero degli argomenti passati da riga di comando, [3] ovvero numero dei parametri posizionali (vedi Esempio 33-2)

$*

Tutti i parametri posizionali visti come un'unica parola

Nota

"$*" dev'essere usata con il quoting.

$@

Simile a $*, ma ogni parametro è una stringa tra apici (quoting), vale a dire, i parametri vengono passati intatti, senza interpretazione o espansione. Questo significa, tra l'altro, che ogni parametro dell'elenco viene considerato come una singola parola.

Nota

Naturalmente, "$@" va usata con il quoting.

Esempio 9-6. arglist: Elenco degli argomenti con $* e $@

#!/bin/bash
# arglist.sh
# Invocate lo script con molti argomenti, come "uno due tre".

E_ERR_ARG=65

if [ ! -n "$1" ]
then
  echo "Utilizzo: `basename $0` argomento1 argomento2 ecc."
  exit $E_ERR_ARG
fi  

echo

indice=1         # Inizializza il contatore.

echo "Elenco degli argomenti con \"\$*\":"
for arg in "$*"  # Non funziona correttamente se "$*" non è tra apici.
do
  echo "Argomento nr.$indice = $arg"
  let "indice+=1"
done             # $* vede tutti gli argomenti come un'unica parola.
echo "Tutto l'elenco come parola singola."

echo

indice=1         # Reimposta il contatore.
                 # Cosa succede se vi dimenticate di farlo?

echo "Elenco degli argomenti con \"\$@\":"
for arg in "$@"
do
  echo "Argomento nr.$indice = $arg"
  let "indice+=1"
done             # $@ vede gli argomenti come parole separate.
echo "Elenco composto da diverse parole."

echo

indice=1         # Reimposta il contatore.

echo "Eleco degli argomenti con \$* (senza quoting):"
for arg in $*
do
  echo "Argomento nr.$indice = $arg"
  let "indice+=1"
done             # $* senza quoting vede gli argomenti come parole separate.
echo "Elenco composto da diverse parole."

exit 0

Dopo uno shift, venendo a mancare il precedente $1, che viene perso, $@ contiene i restanti parametri posizionali.

#!/bin/bash
# Da eseguire con  ./nomescript 1 2 3 4 5

echo "$@"    # 1 2 3 4 5
shift
echo "$@"    # 2 3 4 5
shift
echo "$@"    # 3 4 5

# Ad ogni "shift" viene perso il precedente $1.
# Come conseguenza "$@" contiene i parametri rimanenti.

All'interno degli script di shell, la variabile speciale $@ viene utilizzata come strumento per filtrare un dato input. Il costrutto cat "$@" permette di gestire un input da uno script, dallo stdin o da file forniti come parametri. Vedi Esempio 15-21 e Esempio 15-22.

Attenzione

I parametri $* e $@ talvolta si comportano in modo incoerente e sorprendente. Questo dipende dall'impostazione di $IFS.

Esempio 9-7. Comportamento incoerente di $* e $@

#!/bin/bash

#  Comportamento non corretto delle variabili interne Bash "$*" e "$@",
#+ dipendente dal fatto che vengano utilizzate o meno con il "quoting".
#  Gestione incoerente della suddivisione delle parole e del ritorno a capo.

set -- "Il primo" "secondo" "il:terzo" "" "Il: :quinto"
# Imposta gli argomenti dello script, $1, $2, ecc.

echo

echo 'IFS con il valore preimpostato, utilizzando "$*"'
c=0
for i in "$*"               # tra doppi apici
do echo "$((c+=1)): [$i]"   # Questa riga rimane invariata in tutti gli esempi.
                            # Visualizza gli argomenti.
done
echo ---

echo 'IFS con il valore preimpostato, utilizzando $*'
c=0
for i in $*                 # senza apici
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS con il valore preimpostato, utilizzando "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS con il valore preimpostato, utilizzando $@'
c=0
for i in $@
do echo "$((c+=1)): [$i]"
done
echo ---

IFS=:
echo 'IFS=":", utilizzando "$*"'
c=0
for i in "$*"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando $*'
c=0
for i in $*
do echo "$((c+=1)): [$i]"
done
echo ---

var=$*
echo 'IFS=":", utilizzando "$var" (var=$*)'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando $var (var=$*)'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

var="$*"
echo 'IFS=":", utilizzando $var (var="$*")'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando "$var" (var="$*")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando $@'
c=0
for i in $@
do echo "$((c+=1)): [$i]"
done
echo ---

var=$@
echo 'IFS=":", utilizzando $var (var=$@)'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando "$var" (var=$@)'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

var="$@"
echo 'IFS=":", utilizzando "$var" (var="$@")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilizzando $var (var="$@")'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done

echo

# Provate questo script con ksh o zsh -y.

exit 0

# Script d'esempio di Stephane Chazelas,
# con piccole modifiche apportate dall'autore.

Nota

I parametri $@ e $* differiscono solo quando vengono posti tra doppi apici.

Esempio 9-8. $* e $@ quando $IFS è vuota

#!/bin/bash

#  Se $IFS è impostata, ma vuota, allora "$*" e "$@" non 
#+ visualizzano i parametri posizionali come ci si aspetterebbe.

mecho ()       # Visualizza i parametri posizionali.
{
echo "$1,$2,$3";
}


IFS=""         # Impostata, ma vuota.
set a b c      # Parametri posizionali.

mecho "$*"     # abc,,
mecho $*       # a,b,c

mecho $@       # a,b,c
mecho "$@"     # a,b,c

#  Il comportamento di $* e $@ quando $IFS è vuota dipende da quale 
#+ versione Bash o sh è in esecuzione. È quindi sconsigliabile fare 
#+ affidamento su questa "funzionalità" in uno script.


# Grazie Stephane Chazelas.

exit 0

Altri parametri particolari

$-

Opzioni passate allo script (utilizzando set). Vedi Esempio 14-15.

Attenzione

In origine era un costrutto ksh che è stato adottato da Bash, ma, sfortunatamente, non sembra funzionare in modo attendibile negli script Bash. Un suo possibile uso è quello di eseguire un'autoverifica di interattività.

$!

PID (ID di processo) dell'ultimo job eseguito in background

LOG=$0.log

COMANDO1="sleep 100"

echo "Registra i PID dei comandi in background dello script: $0" >> "$LOG"
# Possono essere così controllati e, se necessario, "uccisi".
echo >> "$LOG"

# Registrazione dei comandi.

echo -n "PID di \"$COMANDO1\":  " >> "$LOG"
${COMANDO1} &
echo $! >> "$LOG"
# PID di "sleep 100":  1506

# Grazie a Jacques Lederer, per il suggerimento.

Uso di $! per il controllo di un job:

possibile_job_bloccante & { sleep ${TIMEOUT}; eval 'kill -9 $!' &> /dev/null; }
# Forza il completamento di un programma mal funzionante.
# Utile, ad esempio, negli script init.

# Grazie a Sylvain Fourmanoit per aver segnalato quest'uso creativo della variabile "!".

O, in alternativa:

# Esempio di Matthew Sage.
# Usato con il suo consenso.
			    
TIMEOUT=30   # Valore di timeout in secondi
conto=0
			    
possibile_job_bloccante & {
        while ((conto < TIMEOUT )); do
		eval '[ ! -d "/proc/$!" ] && ((conto = TIMEOUT))'
		#   /proc directory dove si trovano informazioni sui processi 
		#+ in esecuzione.
		# "-d" verifica se esiste (la directory).
		# Quindi attende che il job in questione venga individuato.
		 ((conto++))
		sleep 1
	done
	eval '[ -d "/proc/$!" ] && kill -15 $!'
	# Se il job bloccante è in esecuzione, lo termina.
}

$_

Variabile speciale impostata all'ultimo argomento del precedente comando eseguito.

Esempio 9-9. Variabile underscore

#!/bin/bash

echo $_              # /bin/bash
                     # digitate solo /bin/bash per eseguire lo script.

du >/dev/null        # Non viene visualizzato alcun output del comando.
echo $_              # du

ls -al >/dev/null    # Non viene visualizzato alcun output del comando.
echo $_              # -al  (ultimo argomento)

: 
echo $_              # :
$?

Exit status di un comando, funzione, o dello stesso script (vedi Esempio 23-7)

$$

ID di processo dello script. La variabile $$ viene spesso usata negli script per creare un nome di file temporaneo "univoco" (vedi Esempio A-13, Esempio 29-6, Esempio 15-28 e Esempio 14-26). Di solito è più semplice che invocare mktemp.

Note

[1]

Naturalmente, il PID dello script in esecuzione è $$.

[2]

In qualche modo analogo alla recorsività, in questo contesto annidamento indica un modello incorporato all'interno di un modello di dimensioni maggiori. Una delle definizioni di annidato, secondo l'edizione del 1913 del Webster's Dictionary, illustra perfettamente il concetto: "Una serie di scatole, casse, o simili, in scala di grandezza, ognuna inserita all'interno della successiva di dimensione maggiore."

[3]

I termini "argomento" e "parametro" vengono spesso usati per indicare la stessa cosa. In questo libro hanno lo stesso, identico significato: quello di una variabile passata ad uno script o ad una funzione.