Quais escopos as variáveis de shell podem ter?

Acabei de me deparar com um problema que me mostra que não tenho certeza do escopo das variáveis do shell.

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

Eu li que isso torna a variável de alguma forma “global” (também conhecida como uma variável de ambiente ), mas eu não entenda o que isso significa. Eu sei sobre globais em programação, mas não em programas distintos.

Além disso, dado que minha configuração de tais variáveis se aplica apenas à sessão shell atual, como eu as configuraria para, digamos, um processo daemon? / p>

Quais escopos as variáveis do shell podem ter?

Resposta

Os processos são organizados em uma árvore: cada processo tem um pai exclusivo, 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 fork / execv chamadas do sistema, 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ê deve export essa variável, de modo 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 só será visível para ele e todos os processos criados depois dessa alteração (sendo uma cópia , como anteriormente disse).

Leve em consideração também que um processo filho pode alterar seu ambiente, por exemplo, pode redefini-lo para os valores padrão, como provavelmente é feito em login para exemplo.

Comentários

  • Ah! OK, vamos ‘ s ver se entendi. No shell, se eu disser FOO=bar, isso define o valor para o processo shell atual. Se eu executar um programa como (bundle install), isso cria um processo filho, que não ‘ consegue acesso a FOO. Mas se eu tivesse dito export FOO=bar, o processo filho (e seus descendentes) teria acesso a ele. Um deles poderia, por sua vez, chamar export FOO=buzz para alterar o valor de seus descendentes ou apenas FOO=buzz para alterar o valor apenas para si mesmo . Está certo?
  • @NathanLong Que ‘ não é exatamente isso: em todos os shells modernos, uma variável é exportada (e qualquer alteração no valor é refletida no ambiente dos descendentes) ou não exportada (ou seja, a variável não está no ambiente). Em particular, se a variável já estiver no ambiente quando o shell for iniciado, ela será exportada.
  • Fiquei um pouco confuso com a frase ” se for um filho alterar o valor de uma variável, o valor alterado só será visível para ela e todos os processos criados após essa alteração “. Seria mais correto dizer ” … visível para ele e todos os seus processos descendentes criados após essa mudança ” – o outro filhos do processo pai, mesmo aqueles iniciados após o processo filho, não são afetados.

Resposta

Em pelo menos em ksh e bash, as variáveis podem ter três escopos, não dois como todas as respostas restantes estão dizendo.

Em além da variável exportada (isto é, ambiente) e escopos de variável não exportada de shell, há também um terceiro mais restrito para variáveis locais de função.

Variáveis declaradas em funções de shell com typeset token são visíveis apenas dentro das funções em que são declarados e em (sub) funções chamadas a partir daí.

Este ksh / bash código:

# 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 nos primeiros três 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, isso ocorre 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 é bastante diferente do Windows, em que você pode usar variáveis de sistema que são totalmente globais e compartilhadas por todos os processos.

Resposta

Eles têm escopo por 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, você está realmente bifurcação de 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 são não passado para processos filho. Ele também pode definir variáveis de “ambiente”, que são. Usar export cria uma variável de ambiente. Em qualquer caso, processos não relacionados (pares do original) não verão a variável; estamos apenas controlando qual proc filho esses veja.

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

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

  • Se B não recebeu (de A) uma variável de ambiente chamada FOO, ele criará uma variável local. Filhos de B não o entenderão (a menos que B chame export).
  • Se B recebeu receber (de A) uma variável de ambiente chamada FOO, ela a modificará por si mesma e seus filhos subsequentemente bifurcados . Filhos de B verão o valor atribuído por B. No entanto, isso não afetará A de forma alguma.

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 meu problema original: eu defini GEM_HOME em meu shell, mas quando liguei bundle install, que criou um processo filho. Como eu não usei export, o processo filho não recebeu o shell “s GEM_HOME.

Desexportando

Você pode “desexportar” uma variável – evitar que ela seja passada aos filhos – 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 

Comentários

  • Quando você diz ” irá modificá-lo para si mesmo e seus filhos ” você deve esclarecer que apenas os filhos criados após a modificação verá o valor modificado.
  • @enzotib – bom ponto. Atualizado.

Resposta

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

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

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

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

http://www.tldp.org/LDP/abs/html/subshells.html

Comentários

  • Ah, então eu estava errado ao usar o termo ” variável de ambiente ” para FOO=bar; você deve usar export para torná-lo um. Pergunta corrigida de acordo.
  • Dê uma olhada no link que eu adicionei.

Resposta

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, sua existência é garantida para todos os processos. Quando um processo é iniciado, ele recebe um cópia do ambiente do pai após a qual os dois se tornam independentes: modificar o ambiente do filho não muda o ambiente do pai e modificar o ambiente do pai não muda o de um filho já existente.

Variáveis do 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 ainda não estiver no ambiente, ela se tornará uma variável de shell . Variáveis de shell existem apenas dentro do processo de shell, semelhante a como as variáveis de Ruby existem apenas em um script de Ruby. Eles nunca são herdados por processos filho.

É aqui que a palavra-chave export entra em ação. Ela copia uma variável do shell para o ambiente do processo do shell, possibilitando que os processos filhos herdem.

Variáveis locais

Variáveis locais são variáveis de shell com escopo para os blocos de código que as contêm. Você declara variáveis locais com a typeset palavra-chave (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. Além disso, as variáveis locais não podem ser exportadas.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *