Quais escopos as variáveis de shell podem ter?

37

Acabei de encontrar um problema que me mostra que não estou claro sobre o escopo das variáveis do shell.

Eu estava tentando usar bundle install , que é um comando Ruby que usa o valor de $GEM_HOME para fazer seu trabalho. Eu tinha definido $GEM_HOME , mas o comando ignorou esse valor até que eu usei export , como em export GEM_HOME=/some/path .

Eu li que isso faz a variável de alguma forma "global" (também conhecida como variável de ambiente ), mas eu não entendo o que isso significa. Eu sei sobre globals na programação, mas não em programas distintos.

Além disso, considerando que minha configuração dessas variáveis se aplica somente à sessão de shell atual, como eu as configuraria para, digamos, um processo daemonizado?

Quais escopos as variáveis de shell podem ter?

    
por Nathan Long 24.12.2011 / 04:07

5 respostas

10

Eles são definidos pelo processo

Os outros respondentes me ajudaram a entender que o escopo da variável shell é sobre processos e seus descendentes .

Quando você digita um comando como ls na linha de comando, na verdade você está bifurcando um processo para executar o programa ls . O novo processo tem seu shell como pai.

Qualquer processo pode ter suas próprias variáveis "locais", que não são passadas para processos filhos. Também pode definir variáveis de "ambiente", que são. Usando export cria uma variável de ambiente. Em ambos os casos, processos não relacionados (pares do original) não verão a variável; estamos controlando apenas o que os processos filhos veem.

Suponha que você tenha um shell bash, que chamaremos A. Você digita bash , que cria um shell bash de processo filho, que chamaremos B. Qualquer coisa que você chamou de export on em A ainda será definido em B.

Agora, em B, você diz FOO=b . Uma das duas coisas acontecerá:

  • Se B não recebeu (de A) uma variável de ambiente chamada FOO , ela criará uma variável local. Filhos de B não o receberão (a menos que B chame export ).
  • Se B fez receber (de A) uma variável de ambiente chamada FOO , irá modificá-la para si e seus subsequentes filhos bifurcados . Filhos de B verão o valor que B designou. No entanto, isso não afetará A.

Aqui está uma demonstração rápida.

FOO=a      # set "local" environment variable
echo $FOO  # 'a'
bash       # forks a child process for the new shell
echo $FOO  # not set
exit       # return to original shell
echo $FOO  # still 'a'

export FOO # make FOO an environment variable
bash       # fork a new "child" shell
echo $FOO  # outputs 'a'
FOO=b      # modifies environment (not local) variable
bash       # fork "grandchild" shell
echo $FOO  # outputs 'b'
exit       # back to child shell
exit       # back to original shell
echo $FOO  # outputs 'a'

Tudo isso explica o meu problema original: eu defino GEM_HOME no meu shell, mas quando chamei bundle install , isso criou um processo filho. Como não usei export , o processo filho não recebeu GEM_HOME do shell.

Não exportar

Você pode "não exportar" uma variável - impedir que ela seja passada para crianças - usando export -n FOO .

export FOO=a   # Set environment variable
bash           # fork a shell
echo $FOO      # outputs 'a'
export -n FOO  # remove environment var for children
bash           # fork a shell
echo $FOO      # Not set
exit           # back up a level
echo $FOO      # outputs 'a' - still a local variable
    
por 04.01.2012 / 21:34
30

Os processos são organizados como uma árvore: cada processo tem um pai único, além de init , que PID é sempre 1 e não tem pai.

A criação de um novo processo geralmente ocorre por meio de um par de chamadas de sistema fork / execv , em que o ambiente do processo filho é uma cópia do processo pai.

Para colocar uma variável no ambiente a partir do shell, você tem que export dessa variável, para que seja visível recursivamente para todos os filhos. Mas esteja ciente de que se um filho alterar o valor de uma variável, o valor alterado é visível apenas para ele e todos os processos criados após essa alteração (sendo uma cópia , como anteriormente disse).

Considere também que um processo filho poderia alterar seu ambiente, por exemplo, poderia redefini-lo para valores padrão, como é provavelmente feito de login , por exemplo.

    
por 24.12.2011 / 08:49
23

Pelo menos em ksh e bash , as variáveis podem ter três escopos, não duas como todas as respostas restantes estão sendo contadas atualmente.

Além da variável exportada (ou seja, do ambiente) e dos escopos variáveis não-portados do shell, há também um terceiro mais restrito para as variáveis locais da função.

As variáveis declaradas nas funções do shell com o token typeset são visíveis somente dentro das funções em que são declaradas e nas (sub) funções chamadas a partir dali.

Este código ksh / bash :

# Create a shell script named /tmp/show that displays the scoped variables values.    
echo 'echo [$environment] [$shell] [$local]' > /tmp/show
chmod +x /tmp/show

# Function local variable declaration
function f
{
    typeset local=three
    echo "in function":
    . /tmp/show 
}

# Global variable declaration
export environment=one

# Unexported (i.e. local) variable declaration
shell=two

# Call the function that creates a function local variable and
# display all three variable values from inside the function
f

# Display the three values from outside the function
echo "in shell":
. /tmp/show 

# Display the same values from a subshell
echo "in subshell":
/tmp/show

# Display the same values from a disconnected shell (simulated here by a clean environment start)
echo "in other shell"
env -i /tmp/show 

produz esta saída:

in function:
[one] [two] [three]
in shell:
[one] [two] []
in subshell:
[one] [] []
in other shell
[] [] []

Como você pode ver, a variável exportada é exibida a partir dos três primeiros locais, as variáveis não exportadas não são exibidas fora do shell atual e a variável local da função não tem valor fora da própria função. O último teste não mostra nenhum valor, isto porque as variáveis exportadas não são compartilhadas entre os shells, ou seja, elas só podem ser herdadas e o valor herdado não pode ser afetado posteriormente pelo shell pai.

Observe que este último comportamento é bem diferente do comportamento do Windows, no qual você pode usar variáveis de sistema totalmente globais e compartilhadas por todos os processos.

    
por 05.01.2012 / 07:03
3

A melhor explicação que posso encontrar sobre exportação é esta:

link

A variável definida em um subshell ou shell filho é visível apenas para o subshell no qual ela é definida. A variável exportada é realmente feita para ser uma variável de ambiente. Então, para ficar claro, seu bundle install executa seu próprio shell, que não vê o $GEM_HOME , a menos que seja feita uma variável environment , também conhecida como exportada.

Você pode dar uma olhada na documentação para o escopo da variável aqui:

link

    
por 24.12.2011 / 04:32
2

Existe uma hierarquia de escopos de variáveis, conforme esperado.

Ambiente

O escopo mais externo é o ambiente. Este é o único escopo gerenciado pelo sistema operacional e, portanto, é garantido que existe para cada processo. Quando um processo é iniciado, ele recebe uma cópia do ambiente do pai após o qual os dois se tornam independentes: modificar o ambiente do filho não altera o do pai e modificar o ambiente do pai não altera o de um filho já existente.

Variáveis da shell

Os shells têm sua própria noção de variáveis. É aqui que as coisas começam a ficar um pouco confusas.

Quando você atribui um valor a uma variável em um shell e essa variável já existe no ambiente, a variável de ambiente recebe o novo valor. No entanto, se a variável não estiver no ambiente, ela se tornará uma variável shell . As variáveis do shell existem apenas no processo de shell, semelhante a como as variáveis do Ruby existem apenas dentro de um script Ruby. Eles nunca são herdados por processos filhos.

Aqui é onde a palavra-chave export entra em jogo. Ele copia uma variável de shell no ambiente do processo do shell, possibilitando a herança de processos filhos.

Variáveis locais

Variáveis locais são variáveis do shell com escopo para os blocos de código que as contêm. Você declara variáveis locais com a palavra-chave typeset (portátil) ou local ou declare (Bash). Como outras variáveis de shell, as variáveis locais não são herdadas por processos filhos. Também variáveis locais não podem ser exportadas.

    
por 12.08.2016 / 07:18