¿Cuál es la diferencia en el uso entre las variables de shell y las variables de entorno?

16

En realidad, no sabía que hay dos tipos diferentes de variables a las que puedo acceder desde la línea de comandos. Todo lo que sabía es que puedo declarar variables como:

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

o accediendo a ellos con un signo $, como:

echo $foo
echo ${bar[1]}

o usando variables incorporadas, como:

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

Ahora, escucho que hay dos (¿al menos?) Tipos de variables: variables de shell y variables de entorno.

  • ¿Cuál es el propósito de tener dos tipos diferentes?
  • ¿Cómo sé de qué tipo es una variable?
  • ¿Cuáles son los usos típicos de cada uno?
Sharkant
fuente

Respuestas:

14

Las variables de entorno son una lista de name=valuepares que existen independientemente del programa (shell, aplicación, daemon ...). Por lo general, los procesos secundarios los heredan (creados por una secuencia fork/ exec): los procesos secundarios obtienen su propia copia de las variables principales.

Las variables de shell existen solo en el contexto de un shell. Solo se heredan en subshells (es decir, cuando el shell se bifurca sin una execoperación). Dependiendo de las características del shell, las variables pueden no solo ser cadenas simples como las del entorno, sino también matrices, variables compuestas, escritas como enteros o de coma flotante, etc.

Cuando se inicia un shell, todas las variables de entorno que hereda de su padre se convierten también en variables de shell (a menos que no sean válidas como variables de shell y otros casos de esquina como los IFSque se reinician con algunos shells), pero estas variables heredadas se etiquetan como exportadas 1 . Eso significa que permanecerán disponibles para procesos secundarios con el valor potencialmente actualizado establecido por el shell. Ese es también el caso con las variables creadas bajo el shell y etiquetadas como exportadas con la exportpalabra clave.

La matriz y otras variables de tipo complejo no se pueden exportar a menos que su nombre y valor se puedan convertir al name=valuepatrón, o cuando exista un mecanismo específico de shell (por ejemplo: bashexporta funciones en el entorno y algunos shells exóticos, no POSIX, como rcy espueden exportar matrices )

Entonces, la principal diferencia entre las variables de entorno y las variables de shell es su alcance: las variables de entorno son globales, mientras que las variables de shell no exportadas son locales para el script.

Tenga en cuenta también que los shells modernos (al menos kshy bash) admiten un tercer alcance de variables de shell. Las variables creadas en funciones con la typesetpalabra clave son locales para esa función (la forma en que se declara la función habilita / deshabilita esta característica ksh, y el comportamiento de persistencia es diferente entre bashy ksh). Ver /unix//a/28349/2594

1 Esto se aplica a los depósitos modernos como ksh, dash, bashy similares. El shell Bourne heredado y los shells de sintaxis no Bourne cshtienen comportamientos diferentes.

jlliagre
fuente
1
Los procesos hijos heredan todo, ya que los hijos se crean como una bifurcación (una copia exacta) de su padre. El punto con las variables de entorno es que se pasan a la execve()llamada del sistema, por lo que se usan (generalmente) para persistir los datos durante la ejecución de otros comandos (en el mismo proceso).
Stéphane Chazelas
No todas las variables de entorno se traducen en variables de shell. Solo aquellos que son válidos como un nombre de variable de shell (y con algunas excepciones como IFSen algunos shells).
Stéphane Chazelas
Shells como rc, espueden exportar matrices usando una codificación adhoc. bashy rctambién puede exportar funciones usando variables de entorno (nuevamente, usando una codificación especial).
Stéphane Chazelas
En ksh93, typesetrestringe el alcance solo en funciones declaradas con la function foo { ...; }sintaxis, no con la foo() cmdsintaxis Bourne ( ) (y su alcance estático no es dinámico como en otros shells).
Stéphane Chazelas
@ StéphaneChazelas ¡Gracias por revisar! Respuesta actualizada para tener en cuenta sus comentarios.
jlliagre
17

Variables de Shell

Las variables de shell son variables cuyo alcance se encuentra en la sesión de shell actual, por ejemplo, en una sesión de shell interactiva o un script.

Puede crear una variable de shell asignando un valor a un nombre no utilizado:

var="hello"

El uso de variables de shell es hacer un seguimiento de los datos en la sesión actual. Las variables de shell generalmente tienen nombres con letras minúsculas.

Variables de entorno

Una variable de entorno es una variable de shell que se ha exportado. Esto significa que será visible como una variable, no solo en la sesión de shell que lo creó, sino también para cualquier proceso (no solo shells) que se inicie desde esa sesión.

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

o

export VAR="hello"

Una vez que se ha exportado una variable de shell, permanece exportada hasta que se desarma, o hasta que su "propiedad de exportación" se elimina (con export -nin bash), por lo que generalmente no es necesario volver a exportarla. Desarmar una variable conunset elimina (no importa si es una variable de entorno o no).

Las matrices y los hashes asociativos en bashy otros shells no se pueden exportar para convertirse en variables de entorno. Las variables de entorno deben ser variables simples cuyos valores son cadenas, y a menudo tienen nombres que consisten en letras mayúsculas.

El uso de variables de entorno es para realizar un seguimiento de los datos en la sesión de shell actual, pero también para permitir que cualquier proceso iniciado tome parte de esos datos. El caso típico de esto es la PATHvariable de entorno, que puede establecerse en el shell y luego ser utilizada por cualquier programa que desee iniciar programas sin especificar una ruta completa a ellos.

La colección de variables de entorno en un proceso a menudo se denomina "el entorno del proceso". Cada proceso tiene su propio entorno.

Las variables de entorno solo se pueden "reenviar", es decir, un proceso hijo nunca puede cambiar las variables de entorno en su proceso padre, y aparte de configurar el entorno para un proceso hijo al iniciarlo, un proceso padre no puede cambiar el entorno existente de un proceso hijo

Las variables de entorno se pueden enumerar con env(sin ningún argumento). Aparte de eso, aparecen igual que las variables de shell no exportadas en una sesión de shell. Esto es un poco especial para el shell ya que la mayoría de los otros lenguajes de programación no suelen mezclar variables "ordinarias" con variables de entorno (ver más abajo).

env también se puede usar para establecer los valores de una o varias variables de entorno en el entorno de un proceso sin establecerlas en la sesión actual:

env CC=clang CXX=clang++ make

Esto comienza makecon la variable de entorno CCestablecida en el valor clangy CXXestablecida enclang++ .

También se puede usar para limpiar el entorno de un proceso:

env -i bash

Este se inicia bashpero no transfiere el entorno actual al nuevo bashproceso (que todavía se tienen las variables de entorno, ya que crea otros nuevos desde sus secuencias de comandos shell de inicialización).

Ejemplo de diferencia

$ 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

Otros idiomas

Hay funciones de biblioteca en la mayoría de los lenguajes de programación que permiten obtener y configurar las variables de entorno. Tenga en cuenta que, dado que las variables de entorno se almacenan como una simple relación clave-valor, generalmente no son "variables" del lenguaje. Un programa puede obtener el valor (que siempre es una cadena de caracteres) correspondiente a una clave (el nombre de la variable de entorno), pero luego tendrá que convertirlo a un entero o cualquier tipo de datos que el lenguaje espera que tenga el valor.

En C, las variables de entorno pueden ser accedidas usando getenv(), setenv(), putenv()y unsetenv(). Las variables creadas con estas rutinas se heredan de la misma manera por cualquier proceso que inicie el programa C.

Otros lenguajes pueden tener estructuras de datos especiales para lograr lo mismo, como el %ENVhash en Perl o la ENVIRONmatriz asociativa en la mayoría de las implementaciones de awk.

Kusalananda
fuente
Gracias, explicación brillantemente clara. Entonces, el entorno es como un gran campo en el que otros programas pueden vivir y ver cada una de las variables de entorno. Algunos programas tienen sus variables privadas, solo ellos mismos pueden verlas, como el shell. pero hay un mecanismo para hacer que las variables privadas sean vistas por todos llamados "exportar". Si esto se entiende bien, ¿de lo único que no estoy seguro es si puede existir más de un entorno al mismo tiempo?
Sharkant
@sharkant Cada proceso en ejecución tiene su propio entorno. Este entorno se hereda del proceso que lo inició. Nunca hay "diálogo cruzado" entre entornos de diferentes procesos. La única forma de cambiar una variable de entorno en un proceso es que el proceso mismo la modifique.
Kusalananda
Gracias por aclarar mi entendimiento. Cada pez dentro de su propia pecera. ¿Qué hay de los procesos que generan otros procesos? ¿Los procesos y sus procesos hijos están todos dentro de un entorno o tienen cada uno el suyo?
Sharkant
1
@sharkant Existen funciones de biblioteca en la mayoría de los idiomas que permiten obtener y configurar las variables de entorno. En C, esto se hace con getenv(), setenv(), putenv()y unsetenv(). Las variables creadas con estas rutinas se heredan de la misma manera por cualquier proceso que inicie el programa C. Otros idiomas pueden tener estructuras de datos especiales para la misma cosa, como %ENVen Perl.
Kusalananda
1
FWIW: la exec*()familia de funciones también puede establecer el entorno para el proceso que se ejecuta.
Satō Katsura
5

Las variables de shell son difíciles de duplicar.

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

Sin embargo, las variables de entorno pueden duplicarse; son solo una lista, y una lista puede tener entradas duplicadas. Aquí hay envdup.cque hacer eso.

#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);
}

Que podemos compilar y ejecutar diciéndole envdupque luego ejecute envpara mostrarnos qué variables de entorno están configuradas ...

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

Esto quizás solo sea útil para encontrar errores u otras rarezas en la forma en que se manejan los programas **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
$ 

Parece que Python 3.6 aquí pasa ciegamente los duplicados (una abstracción permeable) mientras que Perl 5.24 no. ¿Qué hay de las conchas?

$ ./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
$ 

Gosh, ¿qué sucede si sudosolo desinfecta la primera entrada del entorno pero luego se bashejecuta con la segunda? Hola PATHo LD_RUN_PATHexplotar. ¿Está tu sudo(y todo lo demás ) parcheado para ese agujero ? Las vulnerabilidades de seguridad no son "una diferencia anecdótica" ni simplemente "un error" en el programa de llamadas.

thrig
fuente
1
Eso es cierto, pero es una diferencia anecdótica y posiblemente un error del programa que configura la variable duplicada.
jlliagre
1
Ver rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Stéphane Chazelas
0

Una variable de entorno es como una variable de shell , pero no es específica del shell . Todos los procesos en sistemas Unix tienen almacenamiento variable de entorno . La principal diferencia entre las variables de entorno y de shell es: que el sistema operativo pasa todas las variables de entorno de su shell a los programas que ejecuta el shell, mientras que no se puede acceder a las variables de shell en los comandos que ejecuta.

env –El comando le permite ejecutar otro programa en un entorno personalizado sin modificar el actual. Cuando se usa sin argumento, imprimirá una lista de las variables de entorno actuales. printenv –El comando imprime todas o las variables de entorno especificadas. set –El comando establece o desactiva las variables de shell. Cuando se usa sin argumento, imprimirá una lista de todas las variables, incluidas las variables de entorno y de shell, y las funciones de shell. unset –El comando elimina las variables de shell y de entorno. export –El comando establece variables de entorno

Mehdi sellami
fuente