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
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 chameexport
). - 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 usarexport
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.
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 aFOO
. Mas se eu tivesse ditoexport FOO=bar
, o processo filho (e seus descendentes) teria acesso a ele. Um deles poderia, por sua vez, chamarexport FOO=buzz
para alterar o valor de seus descendentes ou apenasFOO=buzz
para alterar o valor apenas para si mesmo . Está certo?