Salta al contenuto principale

export: le variabili d'ambiente

Inviato da tuxsa il

Molti programmi leggono le loro impostazioni da variabili d'ambiente: PATH dice dove cercare i comandi, EDITOR quale editor aprire, LANG in che lingua parlare. Con export si creano queste variabili e si fa in modo che i programmi lanciati dalla shell le vedano.

Cos'è

Una variabile è un nome a cui si associa un valore: NOME=Enzo. Nella shell esistono però due tipi di variabili:

  • le variabili di shell (locali), che vede solo la shell stessa;
  • le variabili d'ambiente (environment), che vengono copiate in ogni programma o script che la shell avvia.

export è il comando interno della shell (come cd e alias) che trasforma una variabile di shell in una variabile d'ambiente, "esportandola" ai processi figli. Gli esempi sono per bash.

Cosa fa

Segna una variabile come esportabile: da quel momento, ogni comando che lanci riceve una copia del suo valore. La copia va solo verso il basso: i processi figli ereditano le variabili, ma ciò che un programma cambia al suo interno non risale alla shell che l'ha lanciato, e non arriva agli altri terminali già aperti.

Come si usa

$ export NOME=valore
$ export NOME
$ export

Le variabili si scrivono di solito in maiuscolo, con lettere, cifre e il trattino basso, e senza spazi attorno al segno =. Si leggono mettendo un $ davanti al nome.

Esempio 1: variabile di shell e variabile d'ambiente

$ SALUTO="Ciao dal terminale"
$ echo "$SALUTO"
Ciao dal terminale
$ bash -c 'echo "Il figlio vede: [$SALUTO]"'
Il figlio vede: []
$ export SALUTO
$ bash -c 'echo "Il figlio vede: [$SALUTO]"'
Il figlio vede: [Ciao dal terminale]

Qui la variabile esiste in shell, ma un programma figlio (una nuova bash) non la vede finché non viene esportata. Dopo export SALUTO il figlio riceve il valore. Si può anche definire ed esportare in un solo passaggio: export SALUTO="Ciao".

Esempio 2: vedere le variabili d'ambiente

$ printenv HOME
/home/enzo
$ printenv | head -n 4
SHELL=/bin/bash
LANG=it_IT.UTF-8
HOME=/home/enzo
USER=enzo
$ env | grep -i '^LANG'
LANG=it_IT.UTF-8
$ export -p | tail -n 2
declare -x SALUTO="Ciao dal terminale"
declare -x SHELL="/bin/bash"
$ echo "$USER lavora in $PWD con $SHELL"
enzo lavora in /home/enzo con /bin/bash

printenv e env mostrano le variabili d'ambiente (printenv NOME ne stampa una sola). export -p (o export senza argomenti) le elenca in forma di comandi declare -x. Il comando set, invece, mostra tutte le variabili della shell, anche quelle non esportate, e le funzioni: l'elenco è lungo. Alcune variabili comuni: HOME, USER, SHELL, PWD, PATH, LANG, EDITOR, TERM.

Esempio 3: il PATH, la variabile più importante

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
$ mkdir -p ~/bin
$ printf '#!/bin/bash\necho "Ciao da mio-comando"\n' > ~/bin/mio-comando
$ chmod +x ~/bin/mio-comando
$ mio-comando
bash: mio-comando: comando non trovato
$ export PATH="$HOME/bin:$PATH"
$ mio-comando
Ciao da mio-comando

PATH è un elenco di cartelle, separate da due punti, in cui la shell cerca i comandi che scrivi. Per usare i propri programmi senza scrivere il percorso completo si aggiunge una cartella (qui ~/bin) davanti a quelle esistenti, mantenendo il resto con :$PATH. Per sapere dove si trova un comando: command -v nome o type nome. Se si dimentica di rimettere $PATH alla fine, la variabile contiene solo la nuova cartella e anche ls sparisce (vedi più avanti).

Esempio 4: una variabile per un solo comando

$ LANG=C date
Mon Oct 26 07:41:03 CET 2026
$ date
lun 26 ott 2026, 07:41:09, CET
$ TZ=America/New_York date
Mon Oct 26 02:41:15 EDT 2026
$ echo "[$LANG]"
[it_IT.UTF-8]

Scrivendo VARIABILE=valore comando sulla stessa riga, la variabile vale solo per quel comando (senza export e senza lasciare tracce nella shell). Qui, LANG=C mostra la data in formato neutro, e TZ in un altro fuso orario. È comodo per prove e per forzare il comportamento di un programma una volta sola.

Esempio 5: l'editor e la lingua di sistema

$ export EDITOR=nano
$ export VISUAL=nano
$ crontab -e
$ export LC_ALL=C
$ ls | sort
$ unset LC_ALL

EDITOR (e VISUAL) dice a crontab -e, git commit, visudo e molti altri quale editor usare, per esempio nano. LC_ALL=C imposta la lingua "neutra" e un ordinamento semplice, basato sui codici dei caratteri: utile negli script, perché sort e altri comandi danno gli stessi risultati su ogni computer (con sort le maiuscole vengono prima delle minuscole). unset elimina una variabile.

Esempio 6: rendere le variabili permanenti

$ nano ~/.bashrc
export EDITOR=nano
export PATH="$HOME/bin:$PATH"
export HISTTIMEFORMAT="%F %T  "
$ source ~/.bashrc
$ echo $EDITOR
nano

Una variabile esportata nel terminale sparisce alla chiusura. Per tenerla, si scrive la riga export in un file che la shell legge all'avvio: ~/.bashrc per i terminali interattivi (con source si rilegge nella sessione aperta), ~/.profile o ~/.bash_profile per i login (come una connessione ssh o una sessione sulla console). Su Debian e Ubuntu il file ~/.profile carica ~/.bashrc, quindi in genere basta il secondo. Per tutti gli utenti, in modo semplice, c'è /etc/environment, con righe NOME=valore (senza export, senza $ e senza espansioni) che si modifica con sudo nano; oppure un file .sh in /etc/profile.d/.

Esempio 7: variabili e sudo

$ export PROGETTO=blog
$ sudo bash -c 'echo "[$PROGETTO]"'
[]
$ sudo PROGETTO=blog bash -c 'echo "[$PROGETTO]"'
[blog]
$ sudo -E bash -c 'echo "[$PROGETTO]"'
[blog]
$ sudo env | grep PROGETTO

sudo per sicurezza ripulisce l'ambiente: le tue variabili non passano al comando eseguito con privilegi, tranne poche (HOME e altre) decise nella configurazione. Per passarne una, si scrive sudo VARIABILE=valore comando. Con sudo -E si chiede di conservare l'intero ambiente, ma può essere vietato dalla configurazione di sudoers, e non è consigliato. Non aspettarti quindi che un sudo comando veda il tuo PATH o il tuo EDITOR.

Esempio 8: segreti e chiavi

$ read -rs -p "Token API: " API_TOKEN; echo
Token API:
$ export API_TOKEN
$ curl -s -H "Authorization: Bearer $API_TOKEN" https://example.org/api/dati
$ unset API_TOKEN

Le variabili d'ambiente sono comode per passare chiavi e token ai programmi (curl, script, strumenti da riga di comando) senza scriverle nei file. Se però scrivi export API_TOKEN=abc123, il comando finisce nella cronologia (vedi l'articolo su history). Con read -rs la shell legge il valore senza mostrarlo e senza salvarlo nella cronologia: -s nasconde ciò che digiti, -r non interpreta le barre inverse, -p mostra il messaggio. Al termine, unset toglie la variabile. Ricorda che l'ambiente di un programma è leggibile dallo stesso utente (con ps e o in /proc/PID/environ), e dall'amministratore: non è un caveau.

Esempio 9: caricare le variabili da un file .env

$ cat .env
DB_HOST=localhost
DB_NAME=blog
APP_ENV=produzione
$ set -a
$ source .env
$ set +a
$ printenv | grep -E '^(DB_|APP_)'
DB_HOST=localhost
DB_NAME=blog
APP_ENV=produzione

Molte applicazioni (anche quelle in docker compose) tengono le impostazioni in un file .env, con una riga NOME=valore per variabile. Per caricarle nella shell ed esportarle tutte in una volta, si attiva set -a (allexport: da qui in poi ogni variabile definita viene esportata), si legge il file con source e si disattiva con set +a. Il file va protetto (chmod 600 .env) e non va incluso nei repository pubblici. Contenuto con spazi o caratteri speciali richiede le virgolette: TITOLO="Il mio blog".

Esempio 10: l'ambiente dei processi non risale

$ cat imposta.sh
#!/bin/bash
export PROVA="impostata dallo script"
echo "Nello script: $PROVA"
$ chmod +x imposta.sh
$ ./imposta.sh
Nello script: impostata dallo script
$ echo "Nella shell: [$PROVA]"
Nella shell: []
$ source imposta.sh
Nello script: impostata dallo script
$ echo "Nella shell: [$PROVA]"
Nella shell: [impostata dallo script]

Uno script viene eseguito in un processo figlio: tutto quello che imposta (variabili, cartella corrente) sparisce quando termina. Se invece lo si legge con source (o con . imposta.sh), le istruzioni vengono eseguite dentro la shell corrente, e le variabili restano. È il motivo per cui gli script di "attivazione" (come quelli degli ambienti virtuali di Python) vanno richiamati con source.

Esempio 11: servizi e cron non leggono ~/.bashrc

$ crontab -l
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
0 3 * * * /home/enzo/bin/backup.sh

$ systemctl cat mio-script | grep -i environment
Environment=APP_ENV=produzione
EnvironmentFile=/etc/mio-script.env

I lavori di cron e i servizi di systemd non passano da ~/.bashrc: hanno un ambiente quasi vuoto. In crontab le variabili si scrivono in cima alla tabella; nelle unità di systemd si usano Environment= (una variabile) e EnvironmentFile= (un file con righe NOME=valore). Dopo una modifica serve systemctl daemon-reload e il riavvio del servizio. Se uno script funziona a mano ma non da cron, la causa è quasi sempre una variabile (spesso PATH) mancante.

Esempio 12: negli script, con valori predefiniti e controlli

#!/bin/bash
export LC_ALL=C

DESTINAZIONE="${BACKUP_DIR:-$HOME/backup}"
: "${API_TOKEN:?Variabile API_TOKEN non impostata}"

mkdir -p "$DESTINAZIONE"
echo "Backup in: $DESTINAZIONE"
curl -fsS -H "Authorization: Bearer $API_TOKEN" \
    https://example.org/api/export -o "$DESTINAZIONE/export.json"

Lo script usa tre costrutti utili. export LC_ALL=C rende i comandi prevedibili. ${BACKUP_DIR:-$HOME/backup} usa il valore della variabile se è impostata e, altrimenti, quello predefinito dopo :-. : "${API_TOKEN:?messaggio}" interrompe lo script con un messaggio d'errore se la variabile manca o è vuota (i due punti iniziali sono un comando che non fa nulla, serve a "usare" l'espansione). L'utente lo esegue così: BACKUP_DIR=/mnt/disco ./backup.sh, oppure dopo aver esportato le variabili.

Esempio 13: esportare anche le funzioni

$ saluta() { echo "Ciao, $1"; }
$ printf 'Anna\nMarco\n' | xargs -I{} bash -c 'saluta {}'
bash: riga 1: saluta: comando non trovato
bash: riga 1: saluta: comando non trovato
$ export -f saluta
$ printf 'Anna\nMarco\n' | xargs -I{} bash -c 'saluta {}'
Ciao, Anna
Ciao, Marco

Con l'opzione -f, export esporta una funzione di bash, in modo che la vedano anche i processi figli (come le bash lanciate da xargs o da find -exec). Con export -n nome una variabile smette di essere esportata, ma resta definita nella shell. Questa funzione è specifica di bash: in altre shell non esiste.

Opzioni utili

  • export NOME=valore: crea ed esporta una variabile; export NOME: esporta una variabile già definita.
  • export / export -p: elenca le variabili esportate.
  • export -n NOME: la rende di nuovo locale alla shell.
  • export -f funzione: esporta una funzione di bash.
  • unset NOME: elimina una variabile (unset -f una funzione).
  • printenv [NOME] / env: mostra l'ambiente (env -i comando lancia un comando con ambiente vuoto).
  • VARIABILE=valore comando: valore valido solo per quel comando.
  • set -a / set +a: esporta automaticamente tutte le variabili definite.
  • readonly NOME=valore: variabile che non si può più modificare.
  • ${VAR:-predefinito}, ${VAR:?messaggio}, ${VAR:+altro}: valori predefiniti e controlli.

Attenzione a

  • Niente spazi attorno al segno uguale: export NOME = valore è un errore, perché la shell interpreta tre elementi separati. Si scrive NOME=valore, e con spazi nel valore NOME="un valore".
  • Non perdere il vecchio PATH: export PATH="$HOME/bin" sostituisce tutto l'elenco e il terminale non trova più né ls né nano (cd continua a funzionare, perché è interno alla shell). Va scritto export PATH="$HOME/bin:$PATH". Se succede, basta chiudere il terminale e aprirne uno nuovo (se la riga è solo nel terminale), oppure correggere il file e usare i percorsi completi, come /usr/bin/nano.
  • Il PATH ripetuto: aggiungere la stessa cartella in ~/.bashrc e poi ricaricare il file più volte (source ~/.bashrc) allunga l'elenco con ripetizioni. Non è grave, ma conviene controllare con echo $PATH.
  • Mai . o una cartella vuota nel PATH: far cercare i comandi nella cartella corrente permette a un file malevolo di sostituirsi a un comando normale. Una voce vuota (come in PATH=:/usr/bin) vale come "cartella corrente".
  • Le virgolette contano: tra apici doppi ("$HOME/bin") la variabile viene sostituita; tra apici singoli ('$HOME/bin') il testo resta così com'è, e il percorso ottenuto non esiste. Quasi sempre vanno usati gli apici doppi, anche quando si legge una variabile (echo "$NOME"), per non rompere i valori con spazi.
  • Vale solo per quel terminale e per i suoi figli: non è una configurazione globale. Una variabile esportata in una finestra non compare in quelle già aperte, né in cron, né in una connessione ssh successiva, a meno che sia scritta in un file di avvio.
  • sudo ripulisce l'ambiente: ciò che esporti non è visibile al comando lanciato con sudo. Passa le variabili necessarie con sudo VARIABILE=valore comando.
  • Segreti nell'ambiente: non scrivere password e token direttamente in export, perché restano nella cronologia; evita di stamparli (env o printenv in un registro o in una richiesta di assistenza possono rivelarli) e non lasciarli in file di avvio leggibili da tutti.
  • Variabili e shell diverse: export NOME=valore funziona in bash e zsh; in fish si usa set -x NOME valore. I file di avvio cambiano (~/.zshrc, ~/.config/fish/config.fish).
  • Nomi riservati: non riusare nomi come PATH, HOME, USER, SHELL o IFS per scopi diversi, e non iniziare i nomi delle tue variabili con un numero. Nelle variabili degli script, i nomi in minuscolo riducono il rischio di sovrapposizioni con quelle d'ambiente.
  • Un'applicazione grafica può non leggere ~/.bashrc: i programmi lanciati dal menu del desktop ereditano l'ambiente della sessione grafica, non quello del terminale. Per quelle variabili serve ~/.profile (da riavviare la sessione) o /etc/environment.

Riepilogo

ComandoCosa fa
NOME=valoreCrea una variabile di shell (non esportata)
export NOME=valoreCrea ed esporta una variabile d'ambiente
export NOMEEsporta una variabile già definita
echo "$NOME"Mostra il valore di una variabile
printenv / envElenca le variabili d'ambiente
export -pElenca le variabili esportate come comandi
unset NOMEElimina una variabile
export -n NOMESmette di esportare la variabile
NOME=valore comandoValore valido solo per quel comando
export PATH="$HOME/bin:$PATH"Aggiunge una cartella al PATH
sudo NOME=valore comandoPassa una variabile a un comando con sudo
read -rs -p "Token: " NOMELegge un valore riservato senza mostrarlo
set -a; source .env; set +aCarica ed esporta le variabili di un file
export -f funzioneEsporta una funzione di bash
source ~/.bashrcRilegge i file di configurazione