Una delle maggiori difficoltà per chi conosce poco Linux è la gestione delle variabili d’ambiente, argomento che di solito è correlato alla questione del profilo della shell, l’esecuzioni di scripts e lo scope delle variabili d’ambiente. Oggi proveremo a fare un po’ di chiarezza sulla questione, cercando di fornire le nozioni essenziali senza dilungarci troppo sui dettagli teorici. Lo scopo sarà quello di fornire istruzioni operative semplici, pronte all’uso, che da sole dovrebbero servire a gestire le esigenze quotidiane di chi lavora su Linux.

Per comodità faremo riferimento alla bash shell: quello che diremo vale in linea di principio anche per le altre shell, con qualche eventuale piccola differenza sintattica.

Cenni teorici

Chi è abituato a configurare le variabili d’ambiente su Windows, saprà che ogni variabile definita a livello di sistema (ad esempio tramite il pannello di controllo) è poi disponibile a tutte le applicazioni. Il discorso è diverso per le variabili locali, definite ad esempio all’interno di un script BATCH, che esistono solamente nel contesto dello script. Purtroppo negli ultimi anni la pratica di usare file BATCH su Windows ha perso terreno, ed è per questo motivo che, quando ci avviciniamo a Linux, a volte facciamo confusione.

In realtà anche su Linux vale lo stesso principio: abbiamo variabili d’ambiente, comuni a tutte le applicazioni e tutte le finestre di shell, e variabili “locali”, definite solamente all’interno di una particolare shell o script. A rigore, ogni finestra di shell o script è di solito un processo a sé stante, per cui sarebbe più corretto dire che le variabili “locali” sono definite all’interno del contesto di un processo specifico. Ma si tratta di una sottigliezza che possiamo trascurare, e pensare, almeno in prima approssimazione, che la gestione di variabili di sistema e variabili locali sia la stessa su Windows e Linux.

La vera differenza è un altra: quando su Linux eseguiamo uno script, o apriamo una nuova shell partendo da un shell precedente (comando bash), di solito viene eseguito un nuovo processo, detto “figlio”, che avrà un set di variabili locali diverse dal processo “padre”. La situazione ricorda le scatole cinesi (o matrioske), dove ogni scatola esterna (il padre) contiene una scatola interna (il figlio) più piccola. L’analogia serve a ricordarci che i processi figli potrebbero avere, almeno all’inizio, meno variabili locali rispetto al processo padre. Abbiamo detto “all’inizio” perché l’affermazione è vera solamente alla “nascita” del figlio. Se iniziamo a definire nuove variabili nel contesto della shell figlio, queste non saranno disponibili nel padre.

Riassumendo, la principale differenza rispetto a Windows è che su Linux dobbiamo precisare di quali variabili locali stiamo parlando. Quando eseguiamo uno script o un comando da terminale, conviene chiederci se dobbiamo “trasmettere” (sarebbe meglio dire “condividere”) delle variabili dal padre al figlio, o viceversa.

Esempi pratici

Per visualizzarre le variabili d’ambiente apriamo un terminale e digitiamo il comando env. Se ci interessa una particolare variabile, possiamo “greppare” il risultato, ad esempio

env | grep JAVA

mostra tutte le variabili d’ambiente che contengono la stringa JAVA nel nome. Per creare una nuova variabile locale, definita solo nella shell corrente, digitiamo

foo=42 set | grep foo

se proviamo a cercare la variabile foo tra le variabili d’ambiente (env | grep foo), vedremo che non è disponibile. Questo è un esempio di differenza tra variabili locali di shell (set) e variabili d’ambiente (env). Se vogliamo trasformare una variabile locale in variabile di sistema dobbiamo esportarla, cioè portarla “fuori” dal contesto corrente e aggiungerla all’ambiente

export foo

dopo l’esportazione verifichiamo di avere la variabile foo definita a livello d’ambiente (env | grep foo). Se invece ci interessa visualizzare il suo contenuto, basta usare il comando echo

echo $foo

notiamo l’uso del dollaro per dire all’interprete dei comandi che vogliamo il contenuto della variabile foo, e non l’eco della stringa “foo”.

L’esercizio di creazione della variabile foo può essere usato anche per chiarire la differenza tra variabili locali del padre e variabili locali del figlio. Proviamo a definire una nuova variabile

prova=ciao

e aprire una shell figlia della precedente, usando il comando bash. Per verificare che ci troviamo nel “figlio”, ovvero nella seconda scatoletta cinese, digitiamo

echo $SHLVL

che dovrebbe ritornare 2 (se torniamo alla shell padre, digitando exit, e ripetiamo il comando qui sopra, ci verrà ritornato il valore 1). Se proviamo a visualizzare il contenuto della variabile prova nella shell figlio, vedremo che essa non è definita. Viceversa, una variabile definita nella shell figlio non risulta disponibile nella shell padre. Anche in questo caso, se una variabile deve esistere in tutti i contesti (padre e figlio), possiamo usare il comando export per trasformala in una variabile d’ambiente. Il modo migliore di verificarlo è fare alcune prove, creando variabili nei diversi livelli della shell e controllando la loro visibilità prima e dopo l'export.

Eseguiamo le prove tranquillamente, senza paura di fare errori.: qualsiasi variabile creata in questo modo verrà cancellata quando chiuderemo tutte le shell che abbiamo aperto. A questo punto, la domanda dovrebbe essere: come possiamo rendere permanente una variabile d’ambiente? In altre parole: come e dove specificare che un certa variabile d'ambiente deve essere sempre definita?

Risolveremo l'enigma nelle prossime puntate, quando vedremo l’uso del bash profile e del comando source.