Bash scripting - espansioni quotabili

Da Guide@Debianizzati.Org.
Versione del 22 lug 2014 alle 10:59 di HAL 9000 (discussione | contributi) (espansioni quotabili)
(diff) ← Versione meno recente | Versione attuale (diff) | Versione più recente → (diff)
Vai alla navigazione Vai alla ricerca
Debian-swirl.png Versioni Compatibili

Tutte le versioni supportate di Debian
Bash scripting

Sommario

  1. Introduzione
  2. Comandi essenziali
  3. Variabili (stringhe)
  4. Caratteri di escape, apici e virgolette
  5. Espansioni in stringhe quotate
  6. Espansioni non quotabili
  7. Istruzioni composte
  8. Funzioni
  9. File descriptor e redirezioni
  10. Segnali

Espansioni in stringhe quotate

Le espansione attivate da $ avvengono con la stessa priorità, e in una stringa quotata sono le uniche permesse, quindi il risultato di un'espansione non può mai essere espanso un'altra volta. Sono permesse le sole espansioni di variabile, già vista, e di parametro, di comando e aritmetica (intera), che saranno trattate in seguito.

Espansione di parametro (stringa)

È una forma modificata dell'espansione di variabile, che permette di operare sulla stringa contenuta con un modificatore.

L'espansione di parametro è utilizzabile ogni volta che lo è quella di variabile, con un'unica differenza: l'espansione di variabile può essere contenuta in alcune espansioni di parametro, purché non al posto del nome della variabile, mentre non è mai possibile annidare più espansioni di parametro.

Modificatori:

  • ${#var} ritorna il numero di caratteri della stringa contenuta in $var. Espande sempre a una singola stringa;
  • ${!var} (non POSIX) ritorna il contenuto della variabile, il cui nome è contenuto in $var (accesso indiretto);
  • ${var:-stringa} (valore di default, se nulla) ritorna il contenuto della variabile, se definita e non è nulla, altrimenti espande stringa che può essere anche un'altra variabile;
  • ${var-stringa} (valore di default, se non definita) ritorna il contenuto della variabile, se definita (anche se nulla), altrimenti espande stringa, che può anche essere un'altra variabile;
  • modificatori di assegnazione e valori alternativi, non trattati perché sconsigliati per la leggibilità del codice e non sono fondamentali. Si noti invece che l'uso dell'espansione di parametro con valore di default è l'unico modo per distinguere una variabile non definita da una nulla;
  • manipolatori di stringa (rimozione, sostituzione, trasformazione in uppercase/lowercase), trattati tra breve.

Esempio:

var2=${var-stringa}   # assegna stringa (perché $var non è definita)
printf %s\\n "$var2"  # stampa: stringa
var=""                # assegna stringa nulla
var2=${var-stringa}   # assegna il contenuto (nullo) di $var (perché è definita)
printf %s\\n "$var2"  # stampa una riga vuota
var2=${var:-stringa}  # assegna stringa (perché $var è nulla)
printf %s\\n "$var2"  # stampa: stringa
rif="var"             # rif contiene il nome (senza $) di $var
var2=${!rif}          # equivalente a: var2=$var
printf %s\\n "$var2"  # stampa: stringa
printf %s\\n ${#var2} # stampa: 7 (la lunghezza di "stringa")

Manipolazione delle stringhe

Per manipolare una stringa, è possibile assegnarla a una variabile per poi effettuarne un'espansione di parametro che manipola la stringa, senza modificare il contenuto della variabile. Per esempio:

VAR="stringa-di-esempio"
printf %s\\n "${VAR#stringa-}"

ritorna il contenuto della variable VAR senza il prefisso "stringa-". VAR non viene modificata, salvo una nuova assegnazione:

VAR=${VAR#stringa-}

ora il prefisso "stringa-" è stato eliminato anche dalla variabile VAR.

I modificatori sono molti, ma possono essere facilmente ricordati se si imparano i fondamentali:

  • # sottrae dall'inizio della stringa (minimale);
  • % sottrae dalla fine della stringa (minimale);
  • / (non POSIX) sostituisce una sottostringa con un'altra (solo la prima volta che viene incontrata);
  • ^ (non POSIX) trasforma in maiuscola la prima lettera della stringa (solo il primo carattere);
  • , (non POSIX) trasforma in minuscola la prima lettera della stringa (solo il primo carattere).

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

Per ottenere gli operatori massimali basta raddoppiare il simbolo:

  • ## sottrae dall'inizio della stringa (massimale);
  • %% sottrae dalla fine della stringa (massimale);
  • // (non POSIX) sostituisce una sottostringa con un'altra (tutte le volte che viene incontrata);
  • ^^ (non POSIX) trasforma in maiuscola la stringa (tutta);
  • ,, (non POSIX) trasforma in minuscola la stringa (tutta).

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

Si noti che le stringhe interne a un'espansione di parametro possono essere delle variabili, ma non altre espansioni di parametro:

# cambia l'estensione nella variabile file
if [ "$file" != "${file%${estensione}}" ]; then
   file=${file%${estensione}}${nuova_estensione}
fi

Le espressioni regolari supportate sono le stesse permesse nelle espansioni di parametro e con lo stesso significato (ossia: ? * [ ]), e con gli operatori di sottrazione c'è differenza tra quelli minimali e massimali soltanto se il pattern contiene uno o più *, che è l'unico carattere speciale che può sostituire un numero qualsiasi (zero o più) di caratteri.

Esempio: alternativa a basename

Quando in uno script ci si deve riferire al nome dello script stesso, è usuale utilizzare il comando esterno basename. Una possibile alternativa:

usage () {
   printf %s\\n "Usage: ${0##*/}"
}

Esempi: manipolazione delle stringhe

VAR="questa.sarebbe.UNA.stringa.di.esempio"
  
                             # Risultato:
  
printf %s\\n "${VAR#*.}"     # sarebbe.UNA.stringa.di.esempio
printf %s\\n "${VAR##*.}"    # esempio
  
printf %s\\n "${VAR%.*}"     # questa.sarebbe.UNA.stringa.di
printf %s\\n "${VAR%%.*}"    # questa
  
printf %s\\n "${VAR/st/LL}"  # queLLa.sarebbe.UNA.stringa.di.esempio
printf %s\\n "${VAR//st/LL}" # queLLa.sarebbe.UNA.LLringa.di.esempio

printf %s\\n "${VAR^*}"      # Questa.sarebbe.UNA.stringa.di.esempio
printf %s\\n "${VAR^^*}"     # QUESTA.SAREBBE.UNA.STRINGA.DI.ESEMPIO

printf %s\\n "${VAR,*}"      # questa.sarebbe.UNA.stringa.di.esempio (invariata)
printf %s\\n "${VAR,,*}"     # questa.sarebbe.una.stringa.di.esempio

Per una spiegazione dettagliata di tutti i modificatori e anche di altri modi di manipolare le stringhe in Bash (ad esempio expr) vedere:

Espansione di comando

Consiste nel trasformare l'output di un comando qualsiasi (interno della shell, esterno, una funzione e anche forme composte) in argomenti per un altro comando, oppure nel valore da assegnare a una variabile. Si effettua racchiudendo un comando tra $(...):

$(comando)

oppure, meno leggibile e sconsigliata tra `...` (su tastiera con layout italiano: Alt Gr + '):

`comando`

Inoltre la prima forma può essere annidata facilmente, mentre la seconda richiederebbe un livello aggiuntivo di escape.

L'output del comando consiste in zero, una o più stringhe: in base agli spazi presenti nell'output prodotto, e in maniera analoga all'espansione a cui sono soggette le variabili. Per trasformare l'output di un comando in una singola stringa è necessario che l'espansione di comando sia quotata, con l'eccezione dell'assegnazione a una variabile.

Esempi di assegnazione:

# assegna alla variabile $oggi la data in formato YYYY_MM_DD
oggi=$(date '+%F')   # senza virgolette
oggi="$(date '+%F')" # equivalente a sopra (non servono nelle assegnazioni)

# associo a testo la dimensione in bytes di $file
testo=$(wc -c -- $file)     # SBAGLIATO! (se la variabile $file contiene spazi o caratteri speciali)
testo=$(wc -c -- "$file")   # le virgolette attorno alla variabile sono necessarie
testo="$(wc -c -- "$file")" # equivalente a sopra
# testo contiene anche altre stringhe, ma sono interessato solo alla prima
bytes=${testo%% *}          # estraggo il primo argomento (espansione di parametro)

Passaggio dell'output dei comandi come argomento:

# stampa stati
printf %s\\n "Login name: $(logname); Name: $(whoami); UID: $(id -ur); EUID: $(id -u); Groups: $(groups)"
printf %s\\n "OS: Debian GNU/Linux $(cat /etc/debian_version) ($(lsb_release -sc))" # uguale a $(lsb_release -sd)
printf %s\\n "Kernel: $(uname) $(uname -r) ($(uname -v))"

Output con a capo finali

Si noti che l'espansione di comando, come anche in sh (POSIX), non espande il comando a tutto l'output prodotto, ma omette sempre gli "a capo" finali. Se da una parte è utile nella maggior parte delle situazioni, può talvolta avere effetti collaterali difficili da prevedere. Si consideri per esempio:

printf %s\\n ciao > ./file_prova # scrive ciao e un "a capo" nel file_prova
testo=$(cat ./file_prova)        # associa il contenuto (SENZA "a capo") a $testo
printf %s\\n "Bytes: ${#testo}"  # NON è la dimensione esatta del file!
printf %s\\n "$testo" |          # invia la stringa a cmp (più un "a capo") per un confronto
  cmp - ./file_prova             # nessun errore!!!
printf %s\\n $?                  # infatti stampa 0
Warning.png ATTENZIONE
Una variabile non può contenere il carattere ASCII numero 0, quindi per i file binari è sempre sbagliato accederli in questo modo. È sconsigliabile anche se non si è certi della loro dimensione, e se si è interessati soltanto alla prima riga si può utilizzare:

read riga < file_prova


Un altro esempio, vogliamo associare il carattere "a capo" a una variabile:

nr="
"                    # funziona, ma occupa più righe e rompe l'indentazione
nr=$'\n'             # funziona (non POSIX) ed è il modo consigliato in bash

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

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

# Attenzione che il carattere aggiunto dev'essere nell'output del comando
nr="$(printf \\n)X"  # SBAGLIATO, $nr contiene solo X

Espansione aritmetica intera

Permette di compiere operazioni aritmetiche tra interi, ritornando sempre una singola stringa contenente l'intero risultante. Può essere quotata, ma non cambia niente.

La sintassi è: $((...))
All'interno delle parentesi è possibile utilizzare:

  • le quattro operazioni: + - * /
  • resto/modulo: %
  • potenza: **
  • variabili da espandere (contenenti valori interi)
  • parentesi per cambiare le priorità degli operatori: ( )

Esempio:

base=9
altezza=5
area=$(($base * $altezza))
printf %s\\n "Area rettangolo: ${area}"       # Stampa 45
printf %s\\n "Area triangolo: $(($area / 2))" # Stampa 22 (RICORDA: solo interi)


Guida scritta da: ~ The_Noise (in Bash tips) Swirl-auth60.png Debianized 60%
Estesa da:
S3v (in Bash tips)
HAL 9000
Verificata da:
S3v (in Bash tips)
HAL 9000 12:59, 22 lug 2014 (CEST)

Verificare ed estendere la guida | Cos'è una guida Debianized