Qual é a diferença no uso entre variáveis ​​de shell e variáveis ​​de ambiente?

16

Na verdade, eu não sabia que existem dois tipos diferentes de variáveis ​​que posso acessar na linha de comando. Tudo o que eu sabia é que posso declarar variáveis ​​como:

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

ou acessá-los com um sinal de $, como:

echo $foo
echo ${bar[1]}

ou usando variáveis ​​embutidas, como:

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

Agora, ouvi dizer que existem dois (pelo menos?) Tipos de variáveis: variáveis ​​de shell e variáveis ​​de ambiente.

  • Qual é o propósito de ter dois tipos diferentes?
  • Como sei que tipo é uma variável?
  • Quais são os usos típicos de cada um?
tubarão
fonte

Respostas:

14

Variáveis ​​de ambiente são uma lista de name=valuepares que existem, independentemente do programa (shell, aplicativo, daemon ...). Eles geralmente são herdados pelos processos filhos (criados por uma sequência fork/ exec): os processos filhos obtêm sua própria cópia das variáveis ​​pai.

As variáveis ​​do shell existem apenas no contexto de um shell. Eles são herdados apenas em subshells (ou seja, quando o shell é bifurcado sem uma execoperação). Dependendo dos recursos do shell, as variáveis ​​podem não ser apenas sequências simples, como as do ambiente, mas também matrizes, compostas, variáveis ​​digitadas como número inteiro ou ponto flutuante etc.

Quando um shell é iniciado, todas as variáveis ​​de ambiente que herda de seu pai tornam-se também variáveis ​​de shell (a menos que sejam inválidas como variáveis ​​de shell e outros casos de canto, como o IFSque é redefinido por alguns shells), mas essas variáveis ​​herdadas são marcadas como exportadas 1 . Isso significa que eles permanecerão disponíveis para processos filhos com o valor potencialmente atualizado definido pelo shell. Esse também é o caso de variáveis ​​criadas sob o shell e marcadas como exportadas com a exportpalavra - chave.

Matriz e outras variáveis ​​de tipo complexo não podem ser exportadas, a menos que seu nome e valor possam ser convertidos para o name=valuepadrão ou quando um mecanismo específico do shell está em vigor (por exemplo: bashexporta funções no ambiente e algumas shells exóticas e não POSIX, como rce espode exportar matrizes )

Portanto, a principal diferença entre variáveis ​​de ambiente e variáveis ​​de shell é seu escopo: variáveis ​​de ambiente são globais, enquanto variáveis ​​de shell não exportadas são locais para o script.

Observe também que os shells modernos (pelo menos kshe bash) suportam um terceiro escopo de variáveis ​​de shell. As variáveis ​​criadas nas funções com a typesetpalavra - chave são locais para essa função (a maneira como a função é declarada ativa / desativa esse recurso em ksh, e o comportamento de persistência é diferente entre bashe ksh). Consulte /unix//a/28349/2594

1 Isso se aplica a cascas modernas como ksh, dash, bashe similar. O shell Bourne herdado e os shell de sintaxe não Bourne, como eles, cshtêm comportamentos diferentes.

jlliagre
fonte
11
Tudo é herdado pelos processos filhos, à medida que os filhos são criados como uma bifurcação (uma cópia exata) de seus pais. O ponto das variáveis ​​de ambiente é que elas são passadas para a execve()chamada do sistema e, portanto, são (geralmente) usadas para persistir dados durante a execução de outros comandos (no mesmo processo).
Stéphane Chazelas
Nem todas as variáveis ​​de ambiente são convertidas em variáveis ​​de shell. Somente aqueles que são válidos como um nome de variável do shell (e com algumas exceções, como IFSem alguns shells).
Stéphane Chazelas
Os shells rc, como , espodem exportar matrizes usando uma codificação adhoc. bashe rctambém pode exportar funções usando variáveis ​​de ambiente (novamente, usando uma codificação especial).
Stéphane Chazelas
Em ksh93, typesetrestringe o escopo apenas em funções declaradas com a function foo { ...; }sintaxe, não com a foo() cmdsintaxe Bourne ( ) (e o escopo estático não é dinâmico como em outros shells).
Stéphane Chazelas
@ StéphaneChazelas Obrigado por revisar! Resposta atualizada para levar em conta suas observações.
jlliagre
17

Variáveis ​​do shell

Variáveis ​​de shell são variáveis ​​cujo escopo está na sessão de shell atual, por exemplo, em uma sessão de shell interativa ou em um script.

Você pode criar uma variável de shell atribuindo um valor a um nome não utilizado:

var="hello"

O uso de variáveis ​​do shell é acompanhar os dados na sessão atual. As variáveis ​​do shell geralmente têm nomes com letras minúsculas.

Variáveis ​​ambientais

Uma variável de ambiente é uma variável de shell que foi exportada. Isso significa que ficará visível como uma variável, não apenas na sessão do shell que a criou, mas também para qualquer processo (não apenas shells) iniciado a partir dessa sessão.

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

ou

export VAR="hello"

Depois que uma variável do shell é exportada, ela permanece exportada até que seja desmarcada ou até que sua "propriedade de exportação" seja removida (com export -nin bash), portanto, geralmente não há necessidade de reexportá-la. Desativar uma variável com a unsetexclui (não importa se é uma variável de ambiente ou não).

Matrizes e hashes associativos bashe outros shells não podem ser exportados para se tornarem variáveis ​​de ambiente. As variáveis ​​de ambiente devem ser variáveis ​​simples cujos valores são cadeias de caracteres e geralmente têm nomes que consistem em letras maiúsculas.

O uso de variáveis ​​de ambiente é acompanhar os dados na sessão atual do shell, mas também permitir que qualquer processo iniciado faça parte desses dados. O caso típico disso é a PATHvariável de ambiente, que pode ser definida no shell e usada posteriormente por qualquer programa que queira iniciar programas sem especificar um caminho completo para eles.

A coleta de variáveis ​​de ambiente em um processo é frequentemente chamada de "o ambiente do processo". Cada processo tem seu próprio ambiente.

As variáveis ​​de ambiente só podem ser "encaminhadas", ou seja, um processo filho nunca pode alterar as variáveis ​​de ambiente em seu processo pai e, além de configurar o ambiente para um processo filho ao iniciá-lo, um processo pai não pode alterar o ambiente existente de um processo filho.

As variáveis ​​de ambiente podem ser listadas com env(sem argumentos). Fora isso, eles aparecem da mesma forma que variáveis ​​de shell não exportadas em uma sessão de shell. Isso é um pouco especial para o shell, já que a maioria das outras linguagens de programação não costuma misturar variáveis ​​"comuns" com variáveis ​​de ambiente (veja abaixo).

env também pode ser usado para definir os valores de uma ou várias variáveis ​​de ambiente no ambiente de um processo sem configurá-los na sessão atual:

env CC=clang CXX=clang++ make

Isso começa makecom a variável de ambiente CCdefinida como o valor clange CXXdefinida como clang++.

Também pode ser usado para limpar o ambiente de um processo:

env -i bash

Isso inicia, bashmas não transfere o ambiente atual para o novo bashprocesso (ele ainda terá variáveis ​​de ambiente à medida que cria novas a partir de seus scripts de inicialização do shell).

Exemplo de diferença

$ 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

Outras línguas

Existem funções de biblioteca na maioria das linguagens de programação que permitem obter e configurar as variáveis ​​de ambiente. Observe que, como as variáveis ​​de ambiente são armazenadas como um relacionamento simples de valor-chave, elas geralmente não são "variáveis" do idioma. Um programa pode buscar o valor (que é sempre uma cadeia de caracteres) correspondente a uma chave (o nome da variável de ambiente), mas precisará convertê-lo em um número inteiro ou em qualquer tipo de dados que o idioma espere que o valor tenha.

Em C, variáveis de ambiente pode ser acedido utilizando getenv(), setenv(), putenv()e unsetenv(). As variáveis ​​criadas com essas rotinas são herdadas da mesma maneira por qualquer processo iniciado pelo programa C.

Outros idiomas podem ter estruturas de dados especiais para realizar a mesma coisa, como o %ENVhash no Perl ou o ENVIRONarray associativo na maioria das implementações do awk.

Kusalananda
fonte
thx, explicação brilhantemente clara. Portanto, o ambiente é como um grande campo em que outros programas podem viver e ver cada uma das variáveis ​​de ambiente. Alguns programas têm suas variáveis ​​privadas, apenas eles mesmos podem vê-los, como o shell. mas existe um mecanismo para fazer com que variáveis ​​privadas sejam vistas por todos chamados "export". Se isso estiver bem entendido, então a única coisa que não tenho certeza é se pode existir mais de um ambiente ao mesmo tempo?
sharkant
@sharkant Cada processo em execução tem seu próprio ambiente. Esse ambiente é herdado do processo que o iniciou. Nunca há "conversa cruzada" entre ambientes de diferentes processos. A única maneira de alterar uma variável de ambiente em um processo é modificando-o pelo próprio processo.
Kusalananda
thx por claryfing meu entendimento. Cada peixe dentro de seu próprio aquário. E os processos que geram outros processos? Os processos e seus processos filhos estão todos dentro de um ambiente ou cada um tem o seu?
sharkant 7/17/17
11
@sharkant Existem funções de biblioteca na maioria dos idiomas que permitem obter e definir as variáveis ​​de ambiente. Em C, isto é feito com getenv(), setenv(), putenv()e unsetenv(). As variáveis ​​criadas com essas rotinas são herdadas da mesma maneira por qualquer processo iniciado pelo programa C. Outros idiomas podem ter estruturas de dados especiais para a mesma coisa, como %ENVno Perl.
Kusalananda
11
FWIW: a exec*()família de funções também pode definir o ambiente para o processo que está sendo executado.
Satō Katsura
5

As variáveis ​​do shell são difíceis de duplicar.

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

Variáveis ​​de ambiente, no entanto, podem ser duplicadas; eles são apenas uma lista e uma lista pode ter entradas duplicadas. Aqui está envdup.cpara fazer exatamente isso.

#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 e executar dizendo envduppara executar envpara nos mostrar quais variáveis ​​de ambiente estão definidas ...

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

Talvez isso seja útil apenas para encontrar bugs ou outras esquisitices na maneira como os programas lidam **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 o Python 3.6 aqui passa cegamente as duplicatas (uma abstração com vazamento), enquanto o Perl 5.24 não. E as 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
$ 

Puxa, o que acontece se sudoapenas higieniza a primeira entrada do ambiente, mas depois bashexecuta com a segunda? Olá PATHou LD_RUN_PATHexplorar. Seu sudo(e tudo mais ?) Está corrigido para esse buraco ? Explorações de segurança não são "uma diferença anedótica" nem apenas "um bug" no programa de chamada.

agitar
fonte
11
Isso é verdade, mas é uma diferença anedótica e, sem dúvida, um bug do programa que define a variável duplicada.
Jlliagre
11
Veja rt.perl.org/Public/Bug/Display.html?id=127158 (CVE-2016-2381)
Stéphane Chazelas
0

Uma variável de ambiente é como uma variável de shell , mas não é específica para o shell . Todos os processos nos sistemas Unix possuem armazenamento variável de ambiente . A principal diferença entre as variáveis ​​de ambiente e o shell é: que o sistema operacional passa todas as variáveis ​​de ambiente do shell para os programas executados pelo shell, enquanto as variáveis ​​do shell não podem ser acessadas nos comandos executados.

env –O comando permite executar outro programa em um ambiente customizado sem modificar o atual. Quando usado sem argumento, imprimirá uma lista das variáveis ​​de ambiente atuais. printenv –O comando imprime todas ou as variáveis ​​de ambiente especificadas. set –O comando define ou desativa as variáveis ​​do shell. Quando usado sem argumento, imprimirá uma lista de todas as variáveis, incluindo variáveis ​​de ambiente e shell, e funções de shell. unset –O comando exclui variáveis ​​de shell e ambiente. export –O comando define variáveis ​​de ambiente

Mehdi sellami
fonte