03: Scalari, quoting e blocchi multiriga

← 02 - Struttura - documenti, mapping, indentazione · Indice · successivo → 04 - Tipi, risoluzione implicita e trappole

Questo è il modulo che elimina più bug per riga letta.

3.1 Tre modi di scrivere una stringa

Plain (senza virgolette): il default:

messaggio: ciao mondo

Nessun escaping possibile. Non può iniziare con la maggior parte dei caratteri speciali, e soprattutto non può contenere : (due punti + spazio) né # (spazio + cancelletto).

Apici singoli: stringa quasi letterale:

percorso: 'C:\Users\ale\file.txt'
apostrofo: 'l''idea'

L’unico escaping possibile è raddoppiare l’apice. Il backslash non ha alcun significato: 'a\nb' è letteralmente a\nb, backslash compreso. È per questo che gli apici singoli sono la scelta giusta per percorsi Windows, regex e template.

Apici doppi: l’unica forma con escape veri:

testo: "prima riga\nseconda riga\ttabulata"
unicode: "\u00e8 anche \U0001F600"

Escape ammessi: \n \t \r \0 \\ \" \/ \b \f \v \e \xNN \uNNNN \UNNNNNNNN e altri. Una lettera non prevista dopo il backslash è un errore, non un backslash letterale.

Quale usare

  • Plain quando il valore è chiaramente testo e non contiene caratteri speciali.
  • Apici singoli ogni volta che devi proteggere un valore: è la scelta di default per il quoting difensivo.
  • Apici doppi solo quando ti servono davvero \n, \t o caratteri Unicode espliciti.

3.2 Quando le virgolette sono obbligatorie

Regola pratica: guarda il primo carattere e i caratteri speciali.

Servono le virgolette se il valore:

CasoEsempio che rompeCorretto
è un valore ambiguo (vedi 04 - Tipi, risoluzione implicita e trappole)paese: NOpaese: 'NO'
contiene : titolo: Casa: dolce casatitolo: 'Casa: dolce casa'
contiene #colore: #FF0000colore: '#FF0000'
inizia con *pwd: *segretopwd: '*segreto'
inizia con &pwd: &chiavepwd: '&chiave'
inizia con !if: !cancelled()if: '!cancelled()'
inizia con %, @, `ps: %PS-Adobeps: '%PS-Adobe'
inizia con { o [tpl: {{ x }}tpl: '{{ x }}'
inizia con - seguito da spazionota: - primonota: '- primo'
ha spazi iniziali o finali che contanot: valore t: 'valore '

✅ verificato, i tre casi più insidiosi:

  • a: &x → {'a': None}. Nessun errore: &x viene letto come un’ancora su un valore vuoto. Una password che inizia con & sparisce in silenzio.
  • color: #FF0000 → {'color': None}.
  • title: Home: dolce casa → ScannerError: mapping values are not allowed here.

Il primo è il peggiore: gli altri due almeno danno un errore o un valore vuoto evidente.

3.3 Blocchi multiriga: | e >

Due caratteri, due semantiche opposte.

| (literal): gli a capo si conservano:

script: |
  echo "primo comando"
  echo "secondo comando"

✅ verificato → 'echo "primo comando"\necho "secondo comando"\n'

> (folded): gli a capo diventano spazi:

descrizione: >
  Questo testo lungo
  viene unito in una riga sola.

✅ verificato → 'Questo testo lungo viene unito in una riga sola.\n'

La regola più importante di questo modulo

Negli script usa sempre |. Con >, echo uno seguito da echo due diventa echo uno echo due: la shell riceve una riga sola e il comando fa una cosa diversa da quella che hai scritto. È un errore classico nei workflow CI.

Dentro un blocco non esiste escaping: #, :, {{ }}, apici: tutto è testo puro. È anche il motivo per cui i blocchi sono il posto giusto per script, template e testi lunghi.

3.4 Chomping: cosa succede all’ultimo a capo

Il modificatore - o + dopo | o > decide che fine fanno gli a capo finali. ✅ tabella verificata eseguendo PyYAML:

FormaA capo interniA capo finaleRighe vuote finali
|conservatiunoeliminate
|-conservatinessunoeliminate
|+conservaticonservatoconservate tutte
>diventano spaziunoeliminate
>-diventano spazinessunoeliminate
>+diventano spaziconservatoconservate

Quale usare:

  • | (clip) per gli script: il newline finale è quello che si aspetta la shell.
  • |- (strip) quando la stringa finisce dentro un file, una variabile o un messaggio e non vuoi una riga vuota di troppo. È il default sensato per i value_template.
  • |+ (keep) quasi mai.

Il chomping è invisibile nel diff: è la causa numero uno del “il file generato ha una riga vuota in più e non capisco perché”.

3.5 Il dettaglio di > che nessuno conosce

Il folding non si applica alle righe più indentate. ✅ verificato:

testo: >
  riga1
  riga2
 
  para2
    più indentata
  fine

→ 'riga1 riga2\npara2\n più indentata\nfine\n'

Cioè: le righe normali vengono unite, una riga vuota produce un a capo, e una riga con indentazione extra mantiene il suo a capo e i suoi spazi. Se dentro un > “abbellisci” un comando lungo indentandolo, ne cambi il valore.

3.6 Indentation indicator

Se la prima riga del blocco è volutamente indentata più delle altre, YAML si confonde (prende quell’indentazione come base e la toglie a tutte). Si risolve dichiarando esplicitamente il livello:

codice: |2
    questa riga ha due spazi in più
   base

Raro, ma quando serve non c’è alternativa. Nota: la cifra va da 1 a 9, mai 0.

3.7 Un caso reale: template e espressioni

Il quoting non dipende dal motore di template, ma da come inizia il valore:

# Home Assistant / Jinja2: inizia con {{ → YAML lo legge come flow mapping → ERRORE
value_template: {{ states('sensor.temperatura') }}     # ✗ rotto
value_template: "{{ states('sensor.temperatura') }}"   # ✓

✅ verificato: la prima forma dà while constructing a mapping ... found unhashable key.

# GitHub Actions: inizia con $ → nessun problema, le virgolette non servono
run: echo ${{ github.sha }}          # ✓
if: ${{ !cancelled() }}              # ✓
if: '!cancelled()'                   # ✓ (le virgolette qui SERVONO: inizia con !)

Regola generale: se il template contiene apici, usa l’altro tipo di apice per il valore; se ne contiene di entrambi i tipi, passa a un blocco |.

Esercizi

  1. Scrivi lo stesso script di due comandi prima con | e poi con >. Leggi il valore risultante in Python e osserva la differenza.
  2. Metti alla prova il chomping: crea |, |- e |+ con lo stesso contenuto e stampa repr() dei tre valori.
  3. Prendi cinque valori di un tuo file reale e chiediti, per ciascuno, se sopravvivrebbe senza virgolette in entrambe le versioni di YAML.
  4. Scrivi un value_template Jinja2 che contiene sia apici singoli sia doppi. Fallo funzionare.