Tracce
/
Bash
Bash
/
Programma
/
L'importanza delle virgolette
L'

L'importanza delle virgolette in Bash

{one: "1 esercizio", many: "%{count} esercizi", other: "%{count} esercizi"}

Informazioni su L'importanza delle virgolette

Un consiglio che i programmatori bash alle prime armi si sentiranno ripetere continuamente:

Metti tra virgolette le tue variabili!

Perché?

Quando espandi una variabile (come $name), quasi sempre vuoi usare il valore della variabile come un'unica stringa (o un unico numero). Non vuoi che il valore venga trattato inaspettatamente come due o più stringhe separate.

Il quoting

Il quoting è il meccanismo principale per assicurarsi che un pezzo di testo venga trattato come un'unica parola. Esistono due tipi principali di virgolette.

Le virgolette doppie

Le virgolette doppie permettono al loro interno l'espansione dei parametri, l'espansione aritmetica e la sostituzione dei comandi.

echo "This is bash version $BASH_VERSION"

Puoi includere una virgoletta doppia letterale all'interno di una stringa racchiusa tra virgolette doppie facendola precedere da una barra rovesciata.

echo "She said \"Let me teach you about bash.\""

Le virgolette singole

Le virgolette singole non permettono alcuna espansione al loro interno.

echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION

Non è possibile inserire una virgoletta singola all'interno di una stringa racchiusa tra virgolette singole.

L'importanza del quoting

Cosa c'è che non va esattamente in echo $name?

Quando la shell analizza una riga di codice, esegue diversi tipi di espansione, in ordine. Due delle ultime espansioni sono la suddivisione in parole e l'espansione dei nomi di file.

La suddivisione in parole

Qualsiasi risultato di un'espansione di parametri, di una sostituzione di comandi o di un'espansione aritmetica che non è avvenuto all'interno di virgolette doppie è soggetto alla «suddivisione in parole». La shell prende il risultato espanso e lo divide in corrispondenza di qualsiasi sequenza di caratteri presente nel valore di $IFS. Per impostazione predefinita, si tratta di spazio, tabulazione e nuova riga.

Possiamo vederlo in azione con un ciclo della shell:

sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^

L'output è:

1 The quick brown fox jumps.

Ma senza virgolette:

i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

L'output è:

1 The
2 quick
3 brown
4 fox
5 jumps.

Vediamolo con un valore diverso di $IFS:

csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done

L'output è:

first
second
third
Disattivare la suddivisione in parole

Una tecnica per disattivare la suddivisione in parole consiste nell'impostare $IFS a una stringa vuota

sentence="The quick brown fox jumps."
IFS=    # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

L'output è:

1 The quick brown fox jumps.

Tuttavia, lasciare le variabili senza virgolette non è consigliato, a meno che non si disattivi anche l'espansione dei nomi di file. Vedremo come farlo più avanti.

L'espansione dei nomi di file

I pattern «glob» si usano per esprimere un pattern conciso che individua un insieme di file. Per esempio, puoi individuare tutti i file Markdown in una directory con *.md. I pattern glob esistono fin dall'invenzione di Unix.

I caratteri jolly di glob sono:

  • * corrisponde a zero o più caratteri qualsiasi,

  • ? corrisponde esattamente a un carattere qualsiasi,

  • [...] corrisponde esattamente a un carattere dell'insieme di caratteri elencati tra le parentesi quadre.

    Advanced
    Ci sono molti dettagli sugli insiemi di caratteri. Clicca qui per mostrarli.
    • [abc] corrisponde esattamente a un carattere che sia a, b o c.
      • per includere una parentesi quadra chiusa letterale nell'espressione, mettila come primo carattere dopo la parentesi quadra aperta (dopo un eventuale ^)
        • [][] è un insieme di caratteri che corrisponde sia a una parentesi quadra chiusa sia a una aperta.
        • oppure fai l'escape con una barra rovesciata: [[\]]
    • [x-y] corrisponde esattamente a un carattere che sia nell'intervallo da x a y
      • per includere un trattino letterale in un insieme di caratteri, deve essere il primo carattere dopo la parentesi quadra aperta (dopo un eventuale ^) oppure l'ultimo carattere prima della parentesi quadra chiusa.
        • [-xy] o [xy-] corrispondono ciascuno esattamente a un carattere che sia -, x o y
        • oppure fai l'escape con una barra rovesciata: [x\-y]
      • si possono specificare più intervalli, per esempio [0-9A-Fa-f] corrisponde a una cifra esadecimale
    • [^abc] corrisponde esattamente a un carattere che NON è a, b o c,
    • [^x-y] corrisponde esattamente a un carattere che NON è nell'intervallo da x a y
    • [[:character_class:]] corrisponde a un carattere che appartiene alla «character_class» indicata
      • le classi di caratteri disponibili sono:
        • lower: lettere minuscole
        • upper: lettere maiuscole
        • alpha: lettere
        • digit: cifre
        • alnum: lettere e cifre
        • space: spazi bianchi
        • blank: spazi bianchi orizzontali (spazio e tabulazione)
        • xdigit: cifre esadecimali
        • word: caratteri ammessi in un identificatore (lettere, numeri, underscore)
        • punct: punteggiatura
        • cntrl: caratteri di controllo
        • graph: caratteri «visibili»
        • print: caratteri visibili più lo spazio
      • si possono combinare:
        • la classe [[:alpha:]] è composta da [[:lower:][:upper:]]
        • la classe [[:alnum:]] è composta da [[:alpha:][:digit:]]
        • la classe [[:word:]] è composta da [[:alnum:]_]
        • l'underscore (ottale 137) appartiene sia a [[:word:]] sia a [[:punct:]]
        • la classe [[:graph:]] è composta da [[:alnum:][:punct:]]
        • la classe [[:print:]] è composta da [[:graph:] ]: solo lo spazio (ottale 040), non qualsiasi altro spazio bianco.
Iterare su un elenco di file

Puoi iterare su un elenco di file con un ciclo for

for file in *.csv; do
    do_something_with "$file"
done

Nota che la variabile "$file" è racchiusa tra virgolette. Il pattern glob può restituire file che contengono spazi nel nome. Dobbiamo mettere la variabile tra virgolette, in modo che il comando do_something_with riceva esattamente un argomento, il nome del file.

I pattern glob non sono espressioni regolari

I pattern glob non sono espressioni regolari.

  • corrispondere esattamente a un carattere
    • regex .
    • glob ?
  • corrispondere a zero o più caratteri
    • regex .*
    • glob *
      • il pattern glob .* significa «individuare un nome di file in cui il primo carattere è un punto letterale, seguito da zero o più caratteri qualsiasi».
  • corrispondere a uno o più caratteri
    • regex .+
    • glob ?*
  • gli insiemi di caratteri delle regex e quelli dei pattern glob si comportano allo stesso modo.
Advanced

Bash fornisce i pattern estesi, che sono più vicini alle espressioni regolari.

Il quoting e l'espansione dei nomi di file

Ci sono casi in cui vuoi sopprimere l'espansione dei nomi di file. La gestione dell'input dell'utente è uno di questi. Considera questo frammento di codice

read -p "Enter something: " user_input
echo $user_input

Se l'utente digita un asterisco (*), qual è l'output? L'elenco dei file nella directory corrente. Probabilmente non è quello che vuoi fare.

Un altro esempio è passare dei pattern come argomenti a programmi che si aspettano dei pattern. Il programma tr è spesso usato per manipolare il testo. Per convertire in minuscolo il valore di una variabile, puoi fare così

var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'

L'output è:

my puppet

Un errore comune è dimenticare di mettere tra virgolette gli argomenti di tr: tr [:upper:] [:lower:]

Caution

Perché è un problema? I pattern glob senza virgolette vengono espansi nell'elenco dei file che corrispondono. Supponi che chi usa lo script abbia nella directory corrente dei file chiamati p, u e w:

touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]

Questo genera un errore!

Il pattern [:upper:] senza virgolette corrisponde a uno qualsiasi tra :, u, p, e o r. Il pattern [:lower:] senza virgolette corrisponde a uno qualsiasi tra :, l, o, w, e o r. Bash fa corrispondere i file p e u al primo pattern e il file w al secondo, e tr viene chiamato così

echo "$var" | tr p u w

E questo è il numero sbagliato di argomenti per tr.

Disattivare l'espansione dei nomi di file

Puoi usare il comando set per disattivare l'espansione dei nomi di file.

echo *      # prints a list of files
set -f      # disable filename expansion
echo *      # prints a literal asterisk
set +f      # enable filename expansion
Controllare l'espansione dei nomi di file

Ci sono diverse impostazioni per il comando integrato shopt che controllano come funziona l'espansione dei nomi di file. Un paio di impostazioni interessanti sono:

  • shopt -s nocaseglob: esegue la corrispondenza senza distinguere maiuscole e minuscole
  • shopt -s extglob: abilita i pattern estesi
  • shopt -s nullglob: se nessun file corrisponde, sostituisce il pattern con nulla. Il comportamento predefinito è lasciare il pattern al suo posto come stringa letterale.
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda