Qual è la differenza nell'uso tra variabili shell e variabili d'ambiente?


16

In realtà non sapevo che ci sono due diversi tipi di variabili a cui posso accedere dalla riga di comando. Tutto quello che sapevo è che posso dichiarare variabili come:

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

o accedendo a loro con un segno $, come:

echo $foo
echo ${bar[1]}

o usando variabili integrate, come:

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

Ora, ho sentito che ci sono due (almeno?) Tipi di variabili: variabili shell e variabili d'ambiente.

  • Qual è lo scopo di avere due diversi tipi?
  • Come faccio a sapere quale tipo è una variabile?
  • Quali sono gli usi tipici di ognuno?


Risposte:


14

Le variabili d'ambiente sono un elenco di name=valuecoppie che esistono qualunque sia il programma (shell, applicazione, demone ...). In genere sono ereditati da processi figlio (creati da una sequenza fork/ exec): i processi figlio ottengono la propria copia delle variabili padre.

Le variabili di shell esistono solo nel contesto di una shell. Sono ereditati solo in subshells (cioè quando la shell è biforcuta senza execun'operazione). A seconda delle caratteristiche della shell, le variabili potrebbero non essere solo stringhe semplici come quelle ambientali ma anche array, variabili composte, tipizzate come numeri interi o virgola mobile, ecc.

All'avvio di una shell, tutte le variabili di ambiente che eredita dal suo genitore diventano anche variabili di shell (a meno che non siano non valide come variabili di shell e altri casi angolari come quelli IFSche vengono ripristinati da alcune shell) ma queste variabili ereditate vengono etichettate come esportate 1 . Ciò significa che rimarranno disponibili per i processi figlio con il valore potenzialmente aggiornato impostato dalla shell. Questo è anche il caso delle variabili create sotto la shell e contrassegnate come esportate con la exportparola chiave.

Le matrici e altre variabili di tipo complesso non possono essere esportate a meno che il loro nome e valore non possano essere convertiti nel name=valuemodello o quando sia presente un meccanismo specifico della shell (ad esempio: bashfunzioni di esportazione nell'ambiente e alcune shell esotiche, non POSIX come rce espossono esportare array ).

Quindi la differenza principale tra variabili di ambiente e variabili di shell è il loro ambito: le variabili di ambiente sono globali mentre le variabili di shell non esportate sono locali allo script.

Si noti inoltre che le shell moderne (almeno kshe bash) supportano un terzo ambito delle variabili shell. Le variabili create nelle funzioni con la typesetparola chiave sono locali per quella funzione (Il modo in cui viene dichiarata la funzione abilita / disabilita questa funzione kshe il comportamento di persistenza è diverso tra bashe ksh). Vedi /unix//a/28349/2594

1 Questo vale per le coperture moderne come ksh, dash, bashe simili. Le shell legacy Bourne e le shell sintattiche non Bourne come cshhanno comportamenti diversi.


1
Tutto è ereditato dai processi figlio mentre i bambini vengono creati come fork (una copia esatta) del loro genitore. Il punto con le variabili di ambiente è che vengono passati alla execve()chiamata di sistema, quindi vengono (in genere) utilizzati per conservare i dati sull'esecuzione di altri comandi (nello stesso processo).
Stéphane Chazelas,

Non tutte le variabili d'ambiente sono tradotte in variabili shell. Solo quelli che sono validi come nome di una variabile di shell (e con alcune eccezioni come IFSin alcune shell).
Stéphane Chazelas,

Conchiglie come rc, espossono esportare array usando una codifica ad hoc. bashe rcpuò anche esportare le funzioni usando le variabili di ambiente (di nuovo, usando una codifica speciale).
Stéphane Chazelas,

In ksh93, typesetlimita l'ambito solo nelle funzioni dichiarate con la function foo { ...; }sintassi, non con la foo() cmdsintassi Bourne ( ) (e il suo ambito statico non è dinamico come in altre shell).
Stéphane Chazelas

@ StéphaneChazelas Grazie per la revisione! Rispondi aggiornato per tenere conto delle tue osservazioni.
jlliagre,

17

Variabili della shell

Le variabili di shell sono variabili il cui ambito è nella sessione di shell corrente, ad esempio in una sessione di shell interattiva o in uno script.

È possibile creare una variabile di shell assegnando un valore a un nome non utilizzato:

var="hello"

L'uso delle variabili della shell è di tenere traccia dei dati nella sessione corrente. Le variabili shell di solito hanno nomi con lettere minuscole.

Variabili ambientali

Una variabile di ambiente è una variabile di shell che è stata esportata. Ciò significa che sarà visibile come variabile, non solo nella sessione della shell che l'ha creata, ma anche per qualsiasi processo (non solo shell) avviato da quella sessione.

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

o

export VAR="hello"

Una volta che una variabile shell è stata esportata, rimane esportata fino a quando non viene disattivata o fino a quando la sua "proprietà di esportazione" viene rimossa (con export -nin bash), quindi di solito non è necessario riesportarla. Disattivare una variabile con la unsetelimina (non importa se si tratta di una variabile di ambiente o meno).

Le matrici e gli hash associativi in bashe altri shell non possono essere esportati per diventare variabili di ambiente. Le variabili di ambiente devono essere variabili semplici i cui valori sono stringhe e spesso hanno nomi costituiti da lettere maiuscole.

L'uso delle variabili di ambiente è per tenere traccia dei dati nella sessione di shell corrente, ma anche per consentire a qualsiasi processo avviato di prendere parte di tali dati. Il caso tipico di ciò è la PATHvariabile d'ambiente, che può essere impostata nella shell e successivamente utilizzata da qualsiasi programma che desideri avviare programmi senza specificare un percorso completo per essi.

La raccolta di variabili d'ambiente in un processo viene spesso definita "l'ambiente del processo". Ogni processo ha il suo ambiente.

Le variabili di ambiente possono solo essere "inoltrate", ovvero un processo figlio non può mai modificare le variabili di ambiente nel suo processo padre e, oltre all'avvio dell'ambiente per un processo figlio all'avvio, un processo padre non può modificare l'ambiente esistente di un processo figlio.

Le variabili d'ambiente possono essere elencate con env(senza argomenti). Oltre a ciò, appaiono uguali alle variabili shell non esportate in una sessione shell. Questo è un po 'speciale per la shell poiché la maggior parte degli altri linguaggi di programmazione di solito non mescola variabili "ordinarie" con variabili di ambiente (vedi sotto).

env può anche essere usato per impostare i valori di una o più variabili d'ambiente nell'ambiente di un processo senza impostarle nella sessione corrente:

env CC=clang CXX=clang++ make

Questo inizia makecon la variabile d'ambiente CCimpostata sul valore clange CXXimpostata su clang++.

Può anche essere utilizzato per cancellare l'ambiente per un processo:

env -i bash

Questo inizia bashma non trasferisce l'ambiente corrente per il nuovo bashprocesso (sarà ancora avere variabili d'ambiente in quanto crea nuovi dai suoi script di shell di inizializzazione).

Esempio di differenza

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

Altre lingue

Esistono funzioni di libreria nella maggior parte dei linguaggi di programmazione che consentono di ottenere e impostare le variabili di ambiente. Si noti che poiché le variabili di ambiente sono memorizzate come una semplice relazione chiave-valore, di solito non sono "variabili" della lingua. Un programma può recuperare il valore (che è sempre una stringa di caratteri) corrispondente a una chiave (il nome della variabile d'ambiente), ma dovrà quindi convertirlo in un numero intero o qualunque tipo di dati la lingua si aspetti che abbia il valore.

In C, le variabili di ambiente possono essere accessibili tramite getenv(), setenv(), putenv()e unsetenv(). Le variabili create con queste routine sono ereditate allo stesso modo da qualsiasi processo avviato dal programma C.

Altre lingue possono avere strutture di dati speciali per realizzare la stessa cosa, come l' %ENVhash in Perl o l' ENVIRONarray associativo nella maggior parte delle implementazioni di awk.


grazie, spiegazione brillantemente chiara. Quindi l'ambiente è come un grande campo in cui altri programmi possono vivere e vedere ciascuna delle variabili di ambiente. Alcuni programmi hanno le loro variabili private, solo loro stessi possono vederli, come la shell. ma esiste un meccanismo per rendere visibili le variabili private a tutti chiamati "export". Se questo è ok capito, l'unica cosa di cui non sono sicuro è se può esistere più di un ambiente contemporaneamente?
Sharkant,

@sharkant Ogni processo in esecuzione ha il suo ambiente. Questo ambiente è ereditato dal processo che lo ha avviato. Non esiste mai un "dialogo incrociato" tra ambienti di processi diversi. L'unico modo per cambiare una variabile d'ambiente in un processo è per il processo stesso modificandolo.
Kusalananda

grazie per aver chiarito la mia comprensione. Ogni pesce dentro la sua ciotola di pesce. Che ne dici di Processi che generano altri processi? I processi e i loro processi figlio sono tutti all'interno di un ambiente o ognuno ha il suo?
Sharkant,

1
@sharkant Esistono funzioni di libreria nella maggior parte delle lingue che consentono di ottenere e impostare le variabili di ambiente. In C, questo è fatto con getenv(), setenv(), putenv()e unsetenv(). Le variabili create con queste routine sono ereditate allo stesso modo da qualsiasi processo avviato dal programma C. Altre lingue possono avere strutture di dati speciali per la stessa cosa, come %ENVin Perl.
Kusalananda

1
FWIW: la exec*()famiglia di funzioni può anche impostare l'ambiente per il processo in esecuzione.
Satō Katsura,

5

Le variabili della shell sono difficili da duplicare.

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

Le variabili d'ambiente possono tuttavia essere duplicate; sono solo un elenco e un elenco può avere voci duplicate. Ecco envdup.cper fare proprio questo.

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

Che possiamo compilare ed eseguire dicendo envdupdi eseguire quindi envper mostrarci quali variabili di ambiente sono impostate ...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

Questo è forse utile solo per trovare bug o altre stranezze nel modo in cui i programmi gestiscono **environ.

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

Sembra che Python 3.6 passi qui alla cieca i duplicati (un'astrazione che perde) mentre Perl 5.24 no. E le conchiglie?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

Accidenti, cosa succede se sudosi disinfetta solo la prima voce di ambiente ma si bashesegue con la seconda? Ciao PATHo LD_RUN_PATHsfruttare. Il tuo sudo(e tutto il resto ?) È patchato per quel buco ? Gli exploit di sicurezza non sono né "una differenza aneddotica" né solo "un bug" nel programma chiamante.


1
Questo è vero ma una differenza aneddotica e probabilmente un bug del programma che imposta la variabile duplicata.
jlliagre,


0

Una variabile d'ambiente è come una variabile di shell , ma non è specifica per la shell . Tutti i processi sui sistemi Unix hanno una memoria variabile d'ambiente . La differenza principale tra ambiente e variabili della shell è: che il sistema operativo passa tutte le variabili d'ambiente della shell ai programmi che la shell esegue, mentre non è possibile accedere alle variabili della shell nei comandi eseguiti.

env –Il comando consente di eseguire un altro programma in un ambiente personalizzato senza modificare quello corrente. Se utilizzato senza un argomento, stamperà un elenco delle variabili di ambiente correnti. printenv –Il comando stampa tutte o le variabili di ambiente specificate. set –Il comando imposta o annulla le variabili della shell. Se utilizzato senza un argomento, stamperà un elenco di tutte le variabili, comprese le variabili di ambiente e shell e le funzioni della shell. unset –Il comando elimina le variabili shell e ambiente. export –Il comando imposta le variabili di ambiente

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.