Capitolo 3. Caratteri speciali

Caratteri speciali che si trovano negli script e non solo

#

Commenti. Le righe che iniziano con # (con l'eccezione di #!) sono considerate commenti.

# Questa riga è un commento.

I commenti possono anche essere posti dopo un comando.

echo "Seguirà un commento." # Qui il commento.
#                          ^ Notate lo spazio prima del #

Sono considerati commenti anche quelli che seguono uno o più spazi posti all'inizio di una riga.

      # Questo commento è preceduto da un carattere di tabulazione.

Attenzione

Non è possibile inserire, sulla stessa riga, un comando dopo un commento. Non esiste alcun metodo per terminare un commento in modo che si possa inserire del "codice eseguibile" sulla stessa riga. È indispensabile porre il comando in una nuova riga.

Nota

Naturalmente, un # preceduto da un carattere di escape in un enunciato echo non verrà considerato come un commento. Inoltre, il # compare in alcuni costrutti di sostituzione di parametro e nelle espressioni con costanti numeriche.

echo "Il presente  # non inizia un commento."
echo 'Il presente  # non inizia un commento.'
echo Il presente  \# non inizia un commento.
echo Il presente   # inizia un commento.

echo ${PATH#*:}       # È una sostituzione di parametro, non un commento.
echo $(( 2#101011 ))  # È una conversione di base, non un commento.

# Grazie, S.C.
I caratteri standard per il quoting e l'escaping (" ' \) evitano la reinterpretazione di #.

Anche alcune operazioni di ricerca di corrispondenza utilizzano il #.

;

Separatore di comandi [punto e virgola]. Permette di inserire due o più comandi sulla stessa riga.

echo ehilà; echo ciao


if [ -x "$nomefile" ]; then    #  Notate che "if" e "then" hanno bisogno del
                               #+ punto e virgola. Perché?
  echo "Il file $nomefile esiste."; cp $nomefile $nomefile.bak
else
  echo "$nomefile non trovato."; touch $nomefile
fi; echo "Verifica di file completata."

Si faccia attenzione che ";", talvolta, deve essere preceduto da un carattere di escape.

;;

Delimitatore in un'opzione case [doppio punto e virgola].

case "$variabile" in
abc)  echo "\$variabile = abc" ;;
xyz)  echo "\$variabile = xyz" ;;
esac

.

Comando "punto" [punto]. Equivale a source (vedi Esempio 14-21). È un builtin bash.

.

"punto", componente dei nomi dei file. Quando si ha a che fare con i nomi dei file si deve sapere che il punto è il prefisso dei file "nascosti", file che un normale comando ls non visualizza.

bash$ touch .file_nascosto
bash$ ls -l
total 10
 -rw-r--r--    1 bozo      4034 Jul 18 22:04 data1.addressbook
 -rw-r--r--    1 bozo      4602 May 25 13:58 data1.addressbook.bak
 -rw-r--r--    1 bozo       877 Dec 17  2000 employment.addressbook


bash$ ls -al
total 14
 drwxrwxr-x    2 bozo  bozo      1024 Aug 29 20:54 ./
 drwx------   52 bozo  bozo      3072 Aug 29 20:51 ../
 -rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 data1.addressbook
 -rw-r--r--    1 bozo  bozo      4602 May 25 13:58 data1.addressbook.bak
 -rw-r--r--    1 bozo  bozo       877 Dec 17  2000 employment.addressbook
 -rw-rw-r--    1 bozo  bozo         0 Aug 29 20:54 .file_nascosto
	        

Se si considerano i nomi delle directory, un punto singolo rappresenta la directory di lavoro corrente, mentre due punti indicano la directory superiore.

bash$ pwd
/home/bozo/projects

bash$ cd .
bash$ pwd
/home/bozo/projects

bash$ cd ..
bash$ pwd
/home/bozo/
	        

Il punto appare spesso come destinazione (directory) nei comandi di spostamento di file.

bash$ cp /home/bozo/current_work/junk/* .
	        

.

"punto" corrispondenza di carattere. Nella ricerca di caratteri, come parte di una espressione regolare, il "punto" verifica un singolo carattere.

"

quoting parziale [doppio apice]. "STRINGA" preserva (dall'interpretazione della shell) la maggior parte dei caratteri speciali che dovessero trovarsi all'interno di STRINGA. Vedi anche Capitolo 5.

'

quoting totale [apice singolo]. 'STRINGA' preserva (dall'interpretazione della shell) tutti i caratteri speciali che dovessero trovarsi all'interno di STRINGA. Questa è una forma di quoting più forte di ". Vedi anche Capitolo 5.

,

operatore virgola. L'operatore virgola concatena una serie di operazioni aritmetiche. Vengono valutate tutte, ma viene restituita solo l'ultima.

let "t2 = ((a = 9, 15 / 3))"  # Imposta "a = 9" e "t2 = 15 / 3".

\

escape [barra inversa]. Strumento per il quoting di caratteri singoli.

\X "preserva" il carattere X. Equivale ad effettuare il "quoting" di X, vale a dire 'X'. La \ si utilizza per il quoting di " e di ', affinché siano interpretati letteralmente.

Vedi Capitolo 5 per una spiegazione approfondita dei caratteri di escape.

/

Separatore nel percorso dei file [barra]. Separa i componenti del nome del file (come in /home/bozo/projects/Makefile).

È anche l'operatore aritmetico di divisione.

`

sostituzione di comando. Il costrutto `comando` rende disponibile l'output di comando per l'assegnamento ad una variabile. È conosciuto anche come apice inverso o apostrofo inverso.

:

comando null [due punti]. È l'equivalente shell di "NOP" (no op, operazione non-far-niente). Può essere considerato un sinonimo del builtin di shell true. Il comando ":" è esso stesso un builtin Bash, ed il suo exit status è "true" (0).

: 
echo $?   # 0

Ciclo infinito:

while :
do
   operazione-1
   operazione-2
   ...
   operazione-n
done

# Uguale a:
#    while true
#    do
#      ...
#    done

Istruzione nulla in un costrutto if/then:

if condizione
then :   # Non fa niente e salta alla prossima istruzione
else
   fa-qualcosa
fi

Fornisce un segnaposto dove è attesa un'operazione binaria, vedi Esempio 8-2 e parametri predefiniti.

: ${nomeutente=`whoami`}
# ${nomeutente=`whoami`}   Senza i : iniziali dà un errore,
#                          tranne se "nomeutente" è un comando o un builtin ...

Fornisce un segnaposto dove è atteso un comando in un here document. Vedi Esempio 18-10.

Valuta una stringa di variabili utilizzando la sostituzione di parametro (come in Esempio 9-15).

: ${HOSTNAME?} ${USER?} ${MAIL?}
#  Visualizza un messaggio d'errore se una, o più, delle variabili 
#+ fondamentali d'ambiente non è impostata.

Espansione di variabile / sostituzione di sottostringa.

In combinazione con >, l'operatore di redirezione, azzera il contenuto di un file, senza cambiarne i permessi. Se il file non esiste, viene creato.

: > data.xxx   # Ora il file "data.xxx" è vuoto.

# Ha lo stesso effetto di cat /dev/null > data.xxx
# Tuttavia non viene generato un nuovo processo poiché ":" è un builtin.
Vedi anche Esempio 15-14.

In combinazione con l'operatore di redirezione >> non ha alcun effetto su un preesistente file di riferimento (: >> file_di_riferimento). Se il file non esiste, viene creato.

Nota

Si utilizza solo con i file regolari, non con con le pipe, i link simbolici ed alcuni file particolari.

Può essere utilizzato per iniziare una riga di commento, sebbene non sia consigliabile. Utilizzando # si disabilita la verifica d'errore sulla parte restante di quella riga, così nulla verrà visualizzato dopo il commento. Questo non succede con :.

: Questo è un commento che genera un errore, (if [ $x -eq 3] ).

I ":" servono anche come separatore di campo nel file /etc/passwd e nella variabile $PATH.

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

!

inverte (o nega) il senso di una verifica o di un exit status [punto esclamativo]. L'operatore ! inverte l'exit status di un comando a cui è stato anteposto (vedi Esempio 6-2). Cambia anche il significato di un operatore di verifica. Può, per esempio, cambiare il senso di "uguale" ( = ) in "non uguale" ( != ). L'operatore ! è una parola chiave Bash.

In un contesto differente, il ! appare anche nelle referenziazioni indirette di variabili.

Ancora, da riga di comando il ! invoca il meccanismo della cronologia di Bash (vedi Appendice J). È da notare che, all'interno di uno script, il meccanismo della cronologia è disabilitato.

*

carattere jolly [asterisco]. Il carattere * serve da "carattere jolly" per l'espansione dei nomi dei file nel globbing. Da solo, ricerca tutti i file di una data directory.

bash$ echo *
abs-book.sgml add-drive.sh agram.sh alias.sh
	      

L' * rappresenta anche tutti i caratteri (o nessuno) in una espressione regolare.

*

operatore aritmetico. Nell'ambito delle operazioni aritmetiche, l' * indica l'operatore di moltiplicazione.

Il doppio asterisco, **, è l'operatore di elevamento a potenza.

?

operatore di verifica. In certe espressioni, il ? indica la verifica di una condizione.

In un costrutto parentesi doppie, il ? viene utilizzato come operatore ternario in stile C. Vedi Esempio 9-31.

Nella sostituzione di parametro, il ? verifica se una variabile è stata impostata.

?

carattere jolly. Il carattere ? serve da "carattere jolly" per un singolo carattere, nell'espansione dei nomi dei file nel globbing, così come rappresenta un singolo carattere in una espressione regolare estesa.

$

Sostituzione di variabile (contenuto di una variabile).

var1=5
var2=23skidoo

echo $var1     # 5
echo $var2     # 23skidoo

Il $ davanti al nome di una variabile rimanda al valore contenuto nella variabile stessa.

$

fine-riga. In una espressione regolare, il "$" rinvia alla fine della riga di testo.

${}
$*, $@
$?

variabile exit status. La variabile $? contiene l'exit status di un comando, di una funzione o dello stesso script.

$$

variabile ID di processo. La variabile $$ contiene l'ID di processo [1] dello script in cui appare.

()

gruppo di comandi.

(a=ciao; echo $a)

Importante

Un elenco di comandi racchiuso da parentesi dà luogo ad una subshell.

Le variabili all'interno delle parentesi, appartenenti quindi alla subshell, non sono visibili dallo script. Il processo genitore, lo script, non può leggere le variabili create nel processo figlio, la subshell.

a=123
( a=321; )	      

echo "a = $a"   # a = 123
# "a" tra parentesi si comporta come una variabile locale.

inizializzazione di array.

Array=(elemento1 elemento2 elemento3)

{xxx,yyy,zzz,...}

Espansione multipla.

cat {file1,file2,file3} > file_unico
# Concatena i file file1, file2 e file3 in file_unico.


cp file22.{txt,backup}
# Copia "file22.txt" in "file22.backup"

Il comando agisce sull'elenco dei file, separati da virgole, specificati tra le parentesi graffe. [2] L'espansione dei nomi dei file (il globbing) viene applicata a quelli elencati tra le parentesi.

Attenzione

Non è consentito alcuno spazio dentro le parentesi, tranne il caso in cui si utilizzi il "quoting" o se preceduto da un carattere di escape.

echo {file1,file2}\ :{\ A," B",' C'}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

{a..z}

Espansione multipla estesa.

echo {a..z} # a b c d e f g h i j k l m n o p q r s t u v w x y z
# Visualizza tutti i caratteri tra  a e z.
					  
echo {0..3} # 0 1 2 3
# Visualizza tutti i caratteri tra  0 e 3.

Il costrutto {a..z}, espansione multipla estesa, è una funzionalità introdotta nella versione 3 di Bash.

{}

Blocco di codice [parentesi graffe]. Conosciuto anche come gruppo inline, questo costrutto crea una funzione anonima (una funzione senza un nome). Tuttavia, a differenza di una "normale" funzione, le variabili presenti all'interno del blocco rimangono visibili alla parte restante dello script.

bash$ { local a; 
	      a=123; }
bash: local: can only be used in a 
function
	      

a=123
{ a=321; }
echo "a = $a"   # a = 321   (valore di a nel blocco di codice)

# Grazie, S.C.

La porzione di codice racchiusa tra le parentesi graffe può avere l'I/O rediretto da e verso se stessa.

Esempio 3-1. Blocchi di codice e redirezione I/O

#!/bin/bash
# Legge le righe del file /etc/fstab.

File=/etc/fstab

{
read riga1
read riga2
} < $File

echo "La prima riga di $File è:"
echo "$riga1"
echo
echo "La seconda riga di $File è:"
echo "$riga2"

exit 0

# Ora, come sarebbe possibile verificare i diversi campi di ciascuna riga?
# Suggerimento: usate awk.

Esempio 3-2. Salvare i risultati di un blocco di codice in un file

#!/bin/bash
# rpm-check.sh

# Interroga un file rpm per visualizzarne la descrizione ed il 
#+contenuto, verifica anche se può essere installato.
# Salva l'output in un file.
# 
# Lo script illustra l'utilizzo del blocco di codice.

SUCCESSO=0
E_ERR_ARG=65

if [ -z "$1" ]
then
  echo "Utilizzo: `basename $0` file-rpm"
  exit $E_ERR_ARG
fi  

{ 
  echo
  echo "Descrizione Archivio:"
  rpm -qpi $1       # Richiede la descrizione.
  echo
  echo "Contenuto dell'archivio:"
  rpm -qpl $1       # Richiede il contenuto.
  echo
  rpm -i --test $1  # Verifica se il file rpm può essere installato.
  if [ "$?" -eq $SUCCESSO ]
  then
    echo "$1 può essere installato."
  else
    echo "$1 non può essere installato."
  fi  
  echo
} > "$1.test"       #  Redirige l'output di tutte le istruzioni del blocco
                    #+ in un file.

echo "I risultati della verifica rpm si trovano nel file $1.test"

# Vedere la pagina di manuale di rpm per la spiegazione delle opzioni.

exit 0

Nota

A differenza di un gruppo di comandi racchiuso da (parentesi), visto in precedenza, una porzione di codice all'interno delle {parentesi graffe} solitamente non dà vita ad una subshell. [3]

{}

segnaposto per testo. Usate dopo xargs -i (opzione sostituzione stringhe). Le doppie parentesi graffe {} sostituiscono l'output di un testo.

ls . | xargs -i -t cp ./{} $1
#            ^^         ^^
					
# Dall'esempio "ex42.sh" (copydir.sh).

{} \;

percorso del file. Per lo più utilizzata nei costrutti find. Non è un builtin di shell.

Nota

Il ";" termina la sintassi dell'opzione -exec del comando find. Deve essere preceduto dal carattere di escape per impedirne la reinterpretazione da parte della shell.

[ ]

verifica.

Verifica l'espressione tra [ ]. È da notare che [ è parte del builtin di shell test (ed anche suo sinonimo), non un link al comando esterno /usr/bin/test.

[[ ]]

verifica.

Verifica l'espressione tra [[ ]]. Questa è una parola chiave di shell.

Vedi la disamina sul costrutto [[ ... ]].

[ ]

elemento di un array.

Nell'ambito degli array, le parentesi quadre vengono impiegate nell'impostazione dei singoli elementi di quell'array.

Array[1]=slot_1 
echo ${Array[1]}

[ ]

intervallo di caratteri.

Come parte di un'espressione regolare, le parentesi quadre indicano un intervallo di caratteri da ricercare.

(( ))

espansione di espressioni intere.

Espande e valuta l'espressione intera tra (( )).

Vedi la disamina sul costrutto (( ... )).

> &> >& >> < <>

nome_script >nome_file redirige l'output di nome_script nel file nome_file. Sovrascrive nome_file nel caso fosse già esistente.

comando &>nome_file redirige sia lo stdout che lo stderr di comando in nome_file.

comando >&2 redirige lo stdout di comando nello stderr.

nome_script >>nome_file accoda l'output di nome_script in nome_file. Se nome_file non esiste, viene creato.

[i]<>nome_file apre il file nome_file in lettura e scrittura, e gli assegna il descrittore di file i. Se nome_file non esiste, viene creato.

(comando)>

<(comando)

In un altro ambito, i caratteri "<" e ">" vengono utilizzati come operatori di confronto tra stringhe.

In un altro ambito ancora, i caratteri "<" e ">" vengono utilizzati come operatori di confronto tra interi. Vedi anche Esempio 15-9.

<<

redirezione utilizzata in un here document.

<<<

redirezione utilizzata in una here string.

<, >

Confronto ASCII.

veg1=carote
veg2=pomodori

if [[ "$veg1" < "$veg2" ]]
then
  echo "Sebbene nel dizionario $veg1 preceda $veg2,"
  echo "questo non intacca le mie preferenze culinarie."
else
  echo "Che razza di dizionario stai usando?"
fi

\<, \>

bash$ grep '\<il\>' filetesto

|

pipe. Passa l'output del comando che la precede come input del comando che la segue, o alla shell. È il metodo per concatenare comandi.

echo ls -l | sh
#  Passa l'output di "echo ls -l" alla shell,
#+ con lo stesso risultato di "ls -l".


cat *.lst | sort | uniq
# Unisce ed ordina tutti i file ".lst", dopo di che cancella le righe doppie.

L'output di uno o più comandi può essere collegato con una pipe ad uno script.

#!/bin/bash
# uppercase.sh : Cambia l'input in caratteri maiuscoli.

tr 'a-z' 'A-Z'
#  Per l'intervallo delle lettere deve essere utilizzato il "quoting" per 
#+ impedire di creare file aventi per nome le singole lettere dei nomi 
#+ dei file.

exit 0
Ora si collega l'output di ls -l allo script.
bash$ ls -l | ./uppercase.sh
 -RW-RW-R--    1 BOZO  BOZO       109 APR  7 19:49 1.TXT
 -RW-RW-R--    1 BOZO  BOZO       109 APR 14 16:48 2.TXT
 -RW-R--R--    1 BOZO  BOZO       725 APR 20 20:56 DATA-FILE
	      

Nota

In una pipe, lo stdout di ogni processo deve essere letto come stdin del successivo. Se questo non avviene, il flusso di dati si blocca. La pipe non si comporterà come ci si poteva aspettare.

cat file1 file2 | ls -l | sort 
# L'output proveniente da "cat file1 file2" scompare.

Una pipe viene eseguita come processo figlio e quindi non può modificare le variabili dello script.

variabile="valore_iniziale"
echo "nuovo_valore" | read variabile
echo "variabile = $variabile"     # variabile = valore_iniziale

Se uno dei comandi della pipe abortisce, questo ne determina l'interruzione prematura. Chiamata pipe interrotta, questa condizione invia un segnale SIGPIPE.

>|

forza la redirezione (anche se è stata impostata l'opzione noclobber) . Ciò provoca la sovrascrittura forzata di un file esistente.

||

operatore logico OR. In un costrutto condizionale, l'operatore || restituirà 0 (successo) se almeno una delle condizioni di verifica valutate è vera.

&

Esegue un lavoro in background. Un comando seguito da una & verrà eseguito in background (sullo sfondo).

bash$ sleep 10 &
[1] 850
[1]+  Done                    sleep 10
	      

In uno script possono essere eseguiti in background sia i comandi che i cicli .

Esempio 3-3. Eseguire un ciclo in background

#!/bin/bash
# background-loop.sh

for i in 1 2 3 4 5 6 7 8 9 10            # Primo ciclo.
do
  echo -n "$i "
done & # Esegue questo ciclo in background.
       # Talvolta verrà eseguito, invece, il secondo ciclo.

echo   # Questo 'echo' alcune volte non verrà eseguito.

for i in 11 12 13 14 15 16 17 18 19 20   # Secondo ciclo.
do
  echo -n "$i "
done  

echo   # Questo 'echo' alcune volte non verrà eseguito.

# ======================================================

# Output atteso:
# 1 2 3 4 5 6 7 8 9 10 
# 11 12 13 14 15 16 17 18 19 20 

# Talvolta si potrebbe ottenere:
# 11 12 13 14 15 16 17 18 19 20 
# 1 2 3 4 5 6 7 8 9 10 bozo $
# (Il secondo 'echo' non è stato eseguito. Perché?)

# Occasionalmente anche:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# (Il primo 'echo' non è stato eseguito. Perché?)

#  Molto raramente qualcosa come:
#  11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20 
#  Il ciclo in primo piano (foreground) ha la precedenza su 
#+ quello in background.

exit 0

#  Per divertirsi veramente,
#+ Nasimuddin Ansari suggerisce l'aggiunta di    sleep 1
#+ dopo i comandi   echo -n "$i"   delle righe 6 e 14.

Attenzione

Un comando eseguito in background all'interno di uno script può provocarne l'interruzione, in attesa che venga premuto un tasto. Fortunatamente, per questa eventualità esiste un rimedio.

&&

operatore logico AND . In un costrutto condizionale, l'operatore && restituirà 0 (successo) solo se tutte le condizioni verificate sono vere.

-

opzione, prefisso. Prefisso di opzione di un comando o di un filtro. Prefisso di un operatore.

COMANDO -[Opzione1][Opzione2][...]

ls -al

sort -dfu $nomefile

set -- $variabile

if [ $file1 -ot $file2 ]
then
  echo "Il file $file1 è più vecchio di $file2."
fi

if [ "$a" -eq "$b" ]
then
  echo "$a è uguale a $b."
fi

if [ "$c" -eq 24 -a "$d" -eq 47 ]
then
  echo "$c è uguale a 24 e $d è uguale a 47."
fi

-

redirezione dallo/allo stdin o stdout [trattino].

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# Sposta l'intero contenuto di una directory in un'altra
# [cortesia di Alan Cox <a.cox@swansea.ac.uk>, con una piccola modifica]

# 1) cd /source/directory    Directory sorgente, dove sono contenuti i file che
#                            devono essere spostati.
# 2) &&                      "lista And": se l'operazione 'cd' ha successo, 
#                            allora viene eseguito il comando successivo.
# 3) tar cf - .              L'opzione 'c' del comando di archiviazione 'tar' 
#                            crea un nuovo archivio, l'opzione 'f' (file), 
#                            seguita da '-' designa come file di destinazione 
#                            lo sdtout, e lo fa nella directory corrente ('.').
# 4) |                       Collegato a...
# 5) ( ... )                 subshell
# 6) cd /dest/directory      Cambia alla directory di destinazione.
# 7) &&                      "lista And", come sopra
# 8) tar xpvf -              Scompatta l'archivio ('x'), mantiene i permessi e 
#                            le proprietà dei file ('p'), invia messaggi
#                            dettagliati allo stdout ('v'), leggendo i dati 
#                            dallo stdin ('f' seguito da '-')
#                            Attenzione: 'x' è un comando, 
#                            mentre 'p', 'v' ed 'f' sono opzioni.
# Whew!



# Più elegante, ma equivalente a:
#   cd source-directory
#   tar cf - . | (cd ../dest/directory; tar xpvf -)
#
#     Ottengono lo stesso rirultato anche:
# cp -a /source/directory/* /dest/directory
#     Oppure:
# cp -a /source/directory/* /source/directory/.[^.]* /dest/directory
#     Nel caso ci siano file nascosti in /source/directory.

bunzip2 -c linux-2.6.16.tar.bz2 | tar xvf -
#  --decomprime il file tar --   | --quindi lo passa a "tar"--
#  Se "tar" non è stato aggiornato per trattare "bunzip2",
#+ occorre eseguire l'operazione in due passi successivi utilizzando una pipe.
#  Lo scopo dell'esercizio è di decomprimere i sorgenti del kernel
#+ compressi con "bzip".

Va notato che, in questo contesto, il "-" non è, di per sé un operatore Bash, ma piuttosto un'opzione riconosciuta da alcune utility UNIX che scrivono allo stdout, come tar, cat, ecc.

bash$ echo "qualsiasi cosa" | cat - 
qualsiasi cosa 

Dove è atteso un nome di file, il - redirige l'output allo stdout (talvolta con tar cf), o accetta l'input dallo stdin, invece che da un file. È un metodo per utilizzare l'utility come filtro in una pipe.

bash$ file
Usage: file [-bciknvzL] [-f filename] [-m magicfiles] file...
	      
Eseguito da solo, da riga di comando, file genera un messaggio d'errore.

Occorre aggiungere il "-" per un migliore risultato. L'esempio seguente fa sì che la shell attenda l'input dall'utente.

bash$ file -
abc
standard input:              ASCII text


bash$ file -
#!/bin/bash
standard input:              Bourne-Again shell script text executable
	      
Ora il comando accetta l'input dallo stdin e lo analizza.

Il "-" può essere utilizzato per collegare lo stdout ad altri comandi. Ciò permette alcune acrobazie, come aggiungere righe all'inizio di un file.

Utilizzare diff per confrontare un file con la sezione di un altro:

grep Linux file1 | diff file2 -

Infine, un esempio concreto di come usare il - con tar.

Esempio 3-4. Backup di tutti i file modificati il giorno precedente

#!/bin/bash

#  Salvataggio di tutti i file della directory corrente che sono stati 
#+ modificati nelle ultime 24 ore in un archivio "tarball" (file trattato 
#+ con tar e gzip).

FILEBACKUP=backup-$(date +%d-%m-%Y)
#                 Inserisce la data nel nome del file di salvataggio.
#                 Grazie a Joshua Tschida per l'idea.
archivio=${1:-$FILEBACKUP}
#  Se non viene specificato un nome di file d'archivio da riga di comando,
#+ questo verrà impostato a "backup-GG-MM-AAAA.tar.gz."

tar cvf - `find . -mtime -1 -type f -print` > $archivio.tar
gzip $archivio.tar
echo "Directory $PWD salvata nel file \"$archivio.tar.gz\"."


#  Stephane Chazelas evidenzia che il precedente codice fallisce l'esecuzione
#+ se incontra troppi file o se un qualsiasi nome di file contiene caratteri 
#+ di spaziatura.

# Suggerisce, quindi, le seguenti alternative:
# -------------------------------------------------------------------
#   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archivio.tar"
#      utilizzando la versione GNU di "find".


#   find . -mtime -1 -type f -exec tar rvf "$archivio.tar" '{}' \;
#         portabile su altre versioni UNIX, ma molto più lento.
# -------------------------------------------------------------------


exit 0

Attenzione

Nomi di file che iniziano con "-" possono provocare problemi quando vengono utilizzati con il "-" come operatore di redirezione. Uno script potrebbe verificare questa possibilità ed aggiungere un prefisso adeguato a tali nomi, per esempio ./-NOMEFILE, $PWD/-NOMEFILE o $PATHNAME/-NOMEFILE.

Anche il valore di una variabile che inizia con un - potrebbe creare problemi.

var="-n"
echo $var		
# Ha l'effetto di un  "echo -n", che non visualizza nulla.

-

directory di lavoro precedente. Il comando cd - cambia alla directory di lavoro precedente. Viene utilizzata la variabile d'ambiente $OLDPWD.

Attenzione

Non bisogna confondere il "-" utilizzato in questo senso con l'operatore di redirezione "-" appena discusso. L'interpretazione del "-" dipende dal contesto in cui appare.

-

Meno. Segno meno in una operazione aritmetica.

=

Uguale. Operatore di assegnamento

a=28 
echo $a   # 28

In un contesto differente, il simbolo di "=" è l'operatore di confronto tra stringhe.

+

Più. Operatore aritmetico di addizione.

In un contesto differente, il simbolo + è un operatore di Espressione Regolare.

+

Opzione. Opzione per un comando o un filtro.

Alcuni comandi e builtin utilizzano il segno + per abilitare certe opzioni ed il segno - per disabilitarle.

%

modulo. Modulo (resto di una divisione) , operatore aritmetico.

In un contesto differente, il simbolo % è l'operatore di ricerca di corrispondenza.

~

directory home [tilde]. Corrisponde alla variabile interna $HOME. ~bozo è la directory home di bozo, e ls ~bozo elenca il suo contenuto. ~/ è la directory home dell'utente corrente e ls ~/ elenca il suo contenuto.

bash$ echo ~bozo
/home/bozo

bash$ echo ~
/home/bozo

bash$ echo ~/
/home/bozo/

bash$ echo ~:
/home/bozo:

bash$ echo ~utente-inesistente
~utente-inesistente
	      

~+

directory di lavoro corrente. Corrisponde alla variabile interna $PWD.

~-

directory di lavoro precedente. Corrisponde alla variabile interna $OLDPWD.

=~

verifica di espressione regolare. Questo operatore è stato introdotto con la versione 3 di Bash.

^

inizio-riga. In una espressione regolare, un "^" rinvia all'inizio di una riga di testo.

Caratteri di controllo

modificano il comportamento di un terminale o la visualizzazione di un testo. Un carattere di controllo è la combinazione di CONTROL + tasto (premuti contemporaneamente). Un carattere di controllo può anche essere scritto in notazione ottale o esadecimale, preceduta da un carattere di escape.

Normalmente, i caratteri di controllo, inseriti in uno script, non sono utili.

  • Ctl-B

    Backspace (ritorno non distruttivo).

  • Ctl-C

    Interruzione. Termina un'applicazione in primo piano.

  • Ctl-D

    Uscita dalla shell (simile a exit).

    "EOF" (end of file). Anch'esso termina l'input dallo stdin.

    Durante la digitazione di un testo in una console o in una finestra xterm, Ctl-D cancella il carattere che si trova sotto al cursore. Quando non ci sono più caratteri, Ctl-D determina la prevista uscita dalla sessione. In una finestra xterm, questo ha come effetto la chiusura della finestra stessa.

  • Ctl-G

    "SEGNALE ACUSTICO" (beep). Su alcune vecchie telescriventi faceva suonare veramente una campanella

  • Ctl-H

    Backspace (ritorno distruttivo). Cancella i caratteri che si trovano sotto al cursore nel suo spostamento a ritroso.

    #!/bin/bash
    # Inserire Ctl-H in una stringa.
    
    a="^H^H"                  # Due Ctl-H (backspace).
    echo "abcdef"             # abcdef
    echo -n "abcdef$a "       # abcd f
    # Spazio finale  ^              ^ Doppio backspace
    echo -n "abcdef$a"        # abcdef
    #  Nessuno spazio alla fine       Non viene seguito il backspace (perch´?)
                              # I risultati possono essere piuttosto diversi da 
                              #+ ciò che ci si aspetta.
    echo; echo

  • Ctl-I

    Tabulazione orizzontale.

  • Ctl-J

    Nuova riga (line feed). In uno script, può anche essere espresso in forma ottale -- '\012' o esadecimale -- '\x0a'.

  • Ctl-K

    Tabulazione verticale.

    Durante la digitazione di un testo in una console o in una finestra xterm, Ctl-K cancella i caratteri a partire da quello che si trova sotto al cursore (compreso) fino alla fine della riga. In uno script, Ctl-K potrebbe comportarsi in modo diverso, come nel successivo esempio di Lee Maschmeyer.

  • Ctl-L

    Formfeed (pulisce lo schermo del terminale). Ha lo stesso effetto del comando clear. Se inviato ad una stampante, Ctl-L provoca un avanzamento fino alla fine del foglio.

  • Ctl-M

    A capo.

    #!/bin/bash
    # Grazie a Lee Maschmeyer per l'esempio.
    
    read -n 1 -s -p $'Control-M sposta il cursore all'inizio della riga. Premi Invio. \x0d'
                             # Naturalmente, '0d' è l'equivalente esadecimale di Control-M.
    echo >&2   #  '-s' non visualizza quello che viene digitato,
               #+ quindi è necessario andare a capo esplicitamente.
    
    read -n 1 -s -p $'Control-J sposta il cursore alla riga successiva. \x0a'
               #  '0a' è l'equivalente esadecimale di Control-J (linefeed).
    echo >&2 
    
    ###
    
    read -n 1 -s -p $'E Control-K\x0b lo sposta direttamente in basso.'
    echo >&2   #  Control-K indica la tabulazione verticale.
    
    # Un esempio migliore dell'effetto di una tabulazione verticale è il seguente:
    
    var=$'\x0aQuesta è la riga finale\x0bQuesta è la riga iniziale\x0a'
    echo "$var"
    #  Stesso risultato dell'esempio precedente. Tuttavia:
    echo "$var" | col
    #  Questo provoca l'inversione nella visualizzazione delle righe.
    #  Inoltre spiega il motivo per cui sono stati posti dei line feed all'inizio e
    #+ alla fine della riga: evitare una visualizzazione confusa.
    
    # La spiegazione di Lee Maschmeyer:
    # --------------------------------
    #  Nel primo esempio [di tabulazione verticale] . . . questa esegue
    #+ una semplice visualizzazione alla riga inferiore senza il ritorno a capo.
    #  Ma questo vale solo per i dispositivi, quali la console Linux,
    #+ che non consentono di andare "in senso inverso."
    #  Il vero scopo della TV è quello di andare in SÙ, non in giù.
    #  Ciò può essere sfruttato per stampare dei soprascritti.
    #  L'utility col può essere usata per simulare il corretto comportamento
    #+ di una TV.
    
    exit 0
  • Ctl-Q

    Ripristino (XON).

    Ripristina lo stdin di un terminale.

  • Ctl-S

    Sospensione (XOFF).

    Congela lo stdin di un terminale. (Si usi Ctl-Q per ripristinarlo.)

  • Ctl-U

    Cancella una riga di input, a partire dal cursore in senso inverso fino all'inizio della riga. In alcune impostazioni, Ctl-U cancella l'intera riga di input, indipendentemente dalla posizione del cursore.

  • Ctl-V

    Durante la digitazione di un testo, Ctl-V consente l'inserimento di caratteri di controllo. Ad esempio, le due righe seguenti si equivalgono:

    echo -e '\x0a'
    echo <Ctl-V><Ctl-J>

    Ctl-V è particolarmente utile in un editor di testo.

  • Ctl-W

    Durante la digitazione di un testo in una console o in una finestra xterm, Ctl-W cancella a partire dal carattere che si trova sotto al cursore all'indietro fino al primo spazio incontrato. In alcune impostazioni, Ctl-W cancella all'indietro fino al primo carattere non alfanumerico.

  • Ctl-Z

    Sospende un'applicazione in primo piano.

Spaziatura

serve come divisore, separando comandi o variabili. La spaziatura è formata da spazi, tabulazioni, righe vuote, o una loro qualsiasi combinazione. [4] In alcuni contesti, quale l'assegnamento di variabile, la spaziatura non è consentita e produce un errore di sintassi.

Le righe vuote non hanno alcun affetto sull'azione dello script, sono quindi molto utili per separare visivamente le diverse sezioni funzionali.

$IFS, è la speciale variabile dei separatori dei campi di input per determinati comandi. Il carattere preimpostato è lo spazio.

Per preservare gli spazi presenti in una stringa o in una variabile, si usi il quoting.

Note

[1]

Il PID, o ID di processo, è un numero assegnato al processo in esecuzione. È possibile visualizzare i PID dei processi in esecuzione con il comando ps.

Definizione: un processo è un programma in esecuzione, talvolta chiamato job.

[2]

È la shell che esegue l'espansione delle parentesi graffe. Il comando agisce sul risultato dell'espansione.

[3]

Eccezione: una porzione di codice tra parentesi graffe come parte di una pipe potrebbe essere eseguita come subshell.

ls | { read primariga; read secondariga; }
#  Errore. Il blocco di codice tra le parentesi graffe esegue una subshell,
#+ così l'output di "ls" non può essere passato alle variabili interne 
#+ al blocco.
echo "La prima riga è $primariga; la seconda riga è $secondariga"
# Non funziona.

# Grazie, S.C.

[4]

Anche il linefeed ("a_capo") è un carattere di spaziatura. Questo spiega perché una riga vuota, essendo generata semplicemente da un a_capo, viene considerata una spaziatura.