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

15

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

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

ou acessando-os com um sinal de $, como:

echo $foo
echo ${bar[1]}

ou usando variáveis incorporadas, 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 qual tipo é uma variável?
  • Quais são os usos típicos para cada um deles?
por sharkant 07.05.2017 / 14:58

3 respostas

14

As variáveis de ambiente são uma lista de pares de name=value que existem independentemente do programa (shell, aplicativo, daemon…). Eles são tipicamente herdados por processos filhos (criados por uma sequência fork / exec ): 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 operação exec ). Dependendo dos recursos do shell, as variáveis podem não ser apenas strings simples, como as do ambiente, mas também arrays, variáveis tipadas, compostas, como inteiro ou ponto flutuante, etc.

Quando um shell é iniciado, todas as variáveis de ambiente herdadas de seu pai se tornam também variáveis de shell (a menos que sejam inválidas como variáveis de shell e outros casos como IFS , que são redefinidas por alguns shells), mas essas variáveis herdadas são marcadas conforme exportado 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 palavra-chave export .

Matriz e outras variáveis do tipo complexo não podem ser exportadas, a menos que seu nome e valor possam ser convertidos para o padrão name=value , ou quando um mecanismo específico do shell estiver em vigor (por exemplo: bash exporta funções no ambiente e alguns exóticos , não cascas POSIX como rc e es podem exportar matrizes).

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

Note também que os shells modernos (pelo menos ksh e bash ) suportam um terceiro escopo de variáveis do shell. As variáveis criadas em funções com a palavra-chave typeset 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 bash e ksh ). Consulte o link

1 Isso se aplica a shells modernos como ksh , dash , bash e similares. As conchas de sintaxe de shell Bourne e não Bourne legadas como csh têm comportamentos diferentes.

    
por 07.05.2017 / 17:05
17

Variáveis da shell

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

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

var="hello"

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

Variáveis de ambiente

Uma variável de ambiente é uma variável do shell que foi exportada. Isso significa que ele será visível como uma variável, não apenas na sessão de shell que o 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 shell for exportada, ela permanecerá exportada até que seja desativada ou até que sua "propriedade de exportação" seja removida (com export -n em bash ), portanto, não há necessidade de reexportá-la. Desativar uma variável com unset exclui (não importa se é uma variável de ambiente ou não).

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

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

A coleção de variáveis de ambiente em um processo é geralmente 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 pode nunca alterar as variáveis de ambiente em seu processo pai, além de configurar o ambiente para um processo filho ao iniciá-lo, o processo pai não pode alterar o ambiente existente de um processo filho.

Variáveis de ambiente podem ser listadas com env (sem nenhum argumento). Além disso, eles aparecem da mesma forma que as 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 geralmente não mistura 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 defini-los na sessão atual:

env CC=clang CXX=clang++ make

Isso inicia make com a variável de ambiente CC definida com o valor clang e CXX definido como clang++ .

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

env -i bash

Isso inicia bash , mas não transfere o ambiente atual para o novo processo bash (ele ainda terá variáveis de ambiente, pois cria novos 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

Outros idiomas

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 de valor-chave simples, 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 terá que convertê-lo em um inteiro ou qualquer tipo de dado que a linguagem espere que o valor tenha. / p>

Em C, as variáveis de ambiente podem ser acessadas usando getenv() , setenv() , putenv() e unsetenv() . Variáveis criadas com essas rotinas são herdadas da mesma maneira por qualquer processo que o programa C inicia.

Outros idiomas podem ter estruturas de dados especiais para realizar a mesma coisa, como o %ENV hash em Perl, ou o ENVIRON associative array na maioria das implementações de awk .

    
por 07.05.2017 / 15:11
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.c para 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 envdup para executar env para 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
$ 

Isso talvez seja útil apenas para encontrar bugs ou outras esquisitices em como os programas lidam com **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 gotejante) enquanto o Perl 5.24 não. Que tal 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 sudo apenas limpar a primeira entrada do ambiente, mas bash for executada com a segunda? Olá PATH ou LD_RUN_PATH exploit. Seu sudo (e todo o resto ?) pagado para esse buraco ? As explorações de segurança não são "uma diferença anedótica" nem apenas "um bug" no programa de chamada.

    
por 07.05.2017 / 16:40